fast_paths/
lib.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
20#[macro_use]
21extern crate log;
22
23use serde::{Deserialize, Deserializer, Serialize, Serializer};
24
25pub use crate::constants::*;
26pub use crate::fast_graph::FastGraph;
27pub use crate::fast_graph32::FastGraph32;
28pub use crate::fast_graph_builder::FastGraphBuilder;
29pub use crate::fast_graph_builder::Params;
30pub use crate::fast_graph_builder::ParamsWithOrder;
31pub use crate::input_graph::Edge;
32pub use crate::input_graph::InputGraph;
33pub use crate::path_calculator::PathCalculator;
34pub use crate::shortest_path::ShortestPath;
35
36mod constants;
37#[cfg(test)]
38mod dijkstra;
39mod fast_graph;
40mod fast_graph32;
41mod fast_graph_builder;
42#[cfg(test)]
43mod floyd_warshall;
44mod heap_item;
45mod input_graph;
46mod node_contractor;
47mod path_calculator;
48mod preparation_graph;
49mod shortest_path;
50mod valid_flags;
51mod witness_search;
52
53/// Prepares the given `InputGraph` for fast shortest path calculations.
54pub fn prepare(input_graph: &InputGraph) -> FastGraph {
55    FastGraphBuilder::build(input_graph)
56}
57
58/// Like `prepare()`, but allows specifying some parameters used for the graph preparation.
59pub fn prepare_with_params(input_graph: &InputGraph, params: &Params) -> FastGraph {
60    FastGraphBuilder::build_with_params(input_graph, params)
61}
62
63/// Prepares the given input graph using a fixed node ordering, which can be any permutation
64/// of the node ids. This can be used to speed up the graph preparation if you have done
65/// it for a similar graph with an equal number of nodes. For example if you have changed some
66/// of the edge weights only.
67pub fn prepare_with_order(input_graph: &InputGraph, order: &[NodeId]) -> Result<FastGraph, String> {
68    FastGraphBuilder::build_with_order(input_graph, order)
69}
70
71/// Like `prepare_with_order()`, but allows specifying some parameters used for the graph preparation
72pub fn prepare_with_order_with_params(
73    input_graph: &InputGraph,
74    order: &[NodeId],
75    params: &ParamsWithOrder,
76) -> Result<FastGraph, String> {
77    FastGraphBuilder::build_with_order_with_params(input_graph, order, params)
78}
79
80/// Calculates the shortest path from `source` to `target`.
81pub fn calc_path(fast_graph: &FastGraph, source: NodeId, target: NodeId) -> Option<ShortestPath> {
82    let mut calc = PathCalculator::new(fast_graph.get_num_nodes());
83    calc.calc_path(fast_graph, source, target)
84}
85
86/// Calculates the shortest path from any of the `sources` to any of the `targets`.
87///
88/// The path returned will be the one with minimum weight among all possible paths between the sources
89/// and targets. The sources and targets can also be assigned an initial weight. In this case the
90/// path returned will be the one that minimizes start_weight + path-weight + target_weight. The
91/// weight of the path also includes start_weight and target_weight.
92pub fn calc_path_multiple_sources_and_targets(
93    fast_graph: &FastGraph,
94    sources: Vec<(NodeId, Weight)>,
95    target: Vec<(NodeId, Weight)>,
96) -> Option<ShortestPath> {
97    let mut calc = PathCalculator::new(fast_graph.get_num_nodes());
98    calc.calc_path_multiple_sources_and_targets(fast_graph, sources, target)
99}
100
101/// Creates a `PathCalculator` that can be used to run many shortest path calculations in a row.
102/// This is the preferred way to calculate shortest paths in case you are calculating more than
103/// one path. Use one `PathCalculator` for each thread.
104pub fn create_calculator(fast_graph: &FastGraph) -> PathCalculator {
105    PathCalculator::new(fast_graph.get_num_nodes())
106}
107
108/// Returns the node ordering of a prepared graph. This can be used to run the preparation with
109/// `prepare_with_order()`.
110pub fn get_node_ordering(fast_graph: &FastGraph) -> Vec<NodeId> {
111    fast_graph.get_node_ordering()
112}
113
114/// When serializing a `FastGraph` in a larger struct, use `#[serde(serialize_with =
115/// "fast_paths::serialize_32`)]` to transform the graph to a 32-bit representation. This will use
116/// 50% more RAM than serializing without transformation, but the resulting size will be 50% less.
117/// It will panic if the graph has more than 2^32 nodes or edges or values for weight.
118pub fn serialize_32<S: Serializer>(fg: &FastGraph, s: S) -> Result<S::Ok, S::Error> {
119    FastGraph32::new(fg).serialize(s)
120}
121
122/// When deserializing a `FastGraph` in a larger struct, use `#[serde(deserialize_with =
123/// "fast_paths::deserialize_32`)]` to transform the graph from a 32-bit representation to the
124/// current platform's supported size. This is necessary when serializing on a 64-bit system and
125/// deserializing on a 32-bit system, such as WASM.
126pub fn deserialize_32<'de, D: Deserializer<'de>>(d: D) -> Result<FastGraph, D::Error> {
127    let fg32 = <FastGraph32>::deserialize(d)?;
128    Ok(fg32.convert_to_usize())
129}
130
131#[cfg(test)]
132mod tests {
133    use std::error::Error;
134    use std::fs::{remove_file, File};
135    use std::time::SystemTime;
136
137    use rand::rngs::StdRng;
138    use rand::Rng;
139    use stopwatch::Stopwatch;
140
141    use crate::constants::NodeId;
142    use crate::dijkstra::Dijkstra;
143    use crate::fast_graph::FastGraph;
144    use crate::floyd_warshall::FloydWarshall;
145    use crate::path_calculator::PathCalculator;
146    use crate::preparation_graph::PreparationGraph;
147
148    use super::*;
149    use crate::fast_graph_builder::ParamsWithOrder;
150
151    #[test]
152    fn routing_on_random_graph() {
153        const REPEATS: usize = 100;
154        for _i in 0..REPEATS {
155            run_test_on_random_graph();
156        }
157    }
158
159    fn run_test_on_random_graph() {
160        const NUM_NODES: usize = 50;
161        const NUM_QUERIES: usize = 1_000;
162        const MEAN_DEGREE: f32 = 2.0;
163
164        let mut rng = create_rng();
165        let input_graph = InputGraph::random(&mut rng, NUM_NODES, MEAN_DEGREE);
166        debug!("random graph: \n {:?}", input_graph);
167        let fast_graph = prepare(&input_graph);
168        let mut path_calculator = create_calculator(&fast_graph);
169
170        let dijkstra_graph = PreparationGraph::from_input_graph(&input_graph);
171        let mut dijkstra = Dijkstra::new(input_graph.get_num_nodes());
172
173        let mut fw = FloydWarshall::new(input_graph.get_num_nodes());
174        fw.prepare(&input_graph);
175
176        let mut num_different_paths = 0;
177        for _i in 0..NUM_QUERIES {
178            let source = rng.gen_range(0, input_graph.get_num_nodes());
179            let target = rng.gen_range(0, input_graph.get_num_nodes());
180            let path_fast = path_calculator
181                .calc_path(&fast_graph, source, target)
182                .unwrap_or(ShortestPath::none(source, target));
183            let path_dijkstra = dijkstra
184                .calc_path(&dijkstra_graph, source, target)
185                .unwrap_or(ShortestPath::none(source, target));
186            let weight_fast = path_fast.get_weight();
187            let weight_dijkstra = path_dijkstra.get_weight();
188            let weight_fw = fw.calc_weight(source, target);
189            assert_eq!(
190                weight_fw, weight_fast,
191                "\nNo agreement for routing query from: {} to: {}\nFloyd-Warshall: {}\nCH: {}\
192                 \n Failing graph:\n{:?}",
193                source, target, weight_fw, weight_fast, input_graph
194            );
195            assert_eq!(
196                path_dijkstra, path_fast,
197                "\nNo agreement for routing query from: {} to: {}\nDijkstra: {}\nCH: {}\
198                 \n Failing graph:\n{:?}",
199                source, target, weight_dijkstra, weight_fast, input_graph
200            );
201            if path_dijkstra.get_nodes() != path_fast.get_nodes() {
202                num_different_paths += 1;
203            }
204        }
205        if num_different_paths as f32 > 0.1 * NUM_QUERIES as f32 {
206            panic!(
207                "too many different paths: {}, out of {}, a few different paths can be expected \
208                    because of unambiguous shortest paths, but if there are too many something is \
209                    wrong",
210                num_different_paths, NUM_QUERIES
211            );
212        }
213    }
214
215    #[test]
216    fn routing_with_multiple_sources_and_targets_on_random_graph() {
217        const REPEATS: usize = 20;
218        for _ in 0..REPEATS {
219            const NUM_NODES: usize = 50;
220            const NUM_QUERIES: usize = 1_000;
221            const MEAN_DEGREE: f32 = 2.0;
222            const NUM_SOURCES: usize = 3;
223            const NUM_TARGETS: usize = 3;
224
225            let mut rng = create_rng();
226            let input_graph = InputGraph::random(&mut rng, NUM_NODES, MEAN_DEGREE);
227            debug!("random graph: \n {:?}", input_graph);
228            let fast_graph = prepare(&input_graph);
229            let mut path_calculator = create_calculator(&fast_graph);
230
231            let dijkstra_graph = PreparationGraph::from_input_graph(&input_graph);
232            let mut dijkstra = Dijkstra::new(input_graph.get_num_nodes());
233
234            let mut num_different_paths = 0;
235            let mut num_paths_not_found = 0;
236            for _ in 0..NUM_QUERIES {
237                // This may pick duplicate source nodes, and even duplicate source nodes with
238                // different weights; anyway that shouldn't break anything.
239                let sources =
240                    gen_weighted_nodes(&mut rng, input_graph.get_num_nodes(), NUM_SOURCES);
241                let targets =
242                    gen_weighted_nodes(&mut rng, input_graph.get_num_nodes(), NUM_TARGETS);
243                let fast_path = path_calculator.calc_path_multiple_sources_and_targets(
244                    &fast_graph,
245                    sources.clone(),
246                    targets.clone(),
247                );
248                let mut dijkstra_paths: Vec<(Option<ShortestPath>, Weight, Weight)> = vec![];
249                for (source, source_weight) in &sources {
250                    for (target, target_weight) in &targets {
251                        dijkstra_paths.push((
252                            dijkstra.calc_path(&dijkstra_graph, *source, *target),
253                            *source_weight,
254                            *target_weight,
255                        ))
256                    }
257                }
258
259                let found_dijkstras: Vec<(ShortestPath, Weight, Weight)> = dijkstra_paths
260                    .into_iter()
261                    .filter(|(p, source_weight, target_weight)| {
262                        p.is_some() && *source_weight < WEIGHT_MAX && *target_weight < WEIGHT_MAX
263                    })
264                    .map(|(p, source_weight, target_weight)| {
265                        (p.unwrap(), source_weight, target_weight)
266                    })
267                    .collect();
268
269                // We have to make sure fast_path is as short as the shortest of all dijkstra_paths
270                let shortest_dijkstra_weight = found_dijkstras
271                    .iter()
272                    .map(|(p, source_weight, target_weight)| {
273                        p.get_weight() + source_weight + target_weight
274                    })
275                    .min();
276
277                if shortest_dijkstra_weight.is_none() {
278                    assert!(fast_path.is_none());
279                    num_paths_not_found += 1;
280                    continue;
281                }
282
283                assert!(fast_path.is_some());
284                let f = fast_path.unwrap();
285                let w = shortest_dijkstra_weight.unwrap();
286                assert_eq!(w, f.get_weight(),
287                           "\nfast_path's weight {} does not match the weight of the shortest Dijkstra path {}.\
288                               \nsources: {:?}\
289                               \ntargets: {:?}\
290                               \nFailing graph:\n{:?}",
291                           f.get_weight(), w, sources, targets, input_graph);
292
293                // There can be multiple options with the same weight. fast_path has to match
294                // at least one of them
295                let matching_dijkstras: Vec<(ShortestPath, Weight, Weight)> = found_dijkstras
296                    .into_iter()
297                    .filter(|(p, source_weight, target_weight)| {
298                        p.get_weight() + source_weight + target_weight == f.get_weight()
299                            && p.get_source() == f.get_source()
300                            && p.get_target() == f.get_target()
301                    })
302                    .collect();
303
304                assert!(
305                    matching_dijkstras.len() > 0,
306                    "There has to be at least one Dijkstra path with source,target and weight equal to fast_path"
307                );
308
309                // one of the matching Dijkstra's should have the same nodes as fast_path, but in
310                // some rare cases this might not be true, because there are multiple shortest paths.
311                if !matching_dijkstras
312                    .into_iter()
313                    .any(|(p, _, _)| p.get_nodes() == f.get_nodes())
314                {
315                    num_different_paths += 1;
316                }
317            }
318            if num_paths_not_found as f32 > 0.5 * NUM_QUERIES as f32 {
319                panic!(
320                    "too many paths not found: {}, out of {}",
321                    num_paths_not_found, NUM_QUERIES
322                )
323            }
324            if num_different_paths as f32 > 0.1 * NUM_QUERIES as f32 {
325                panic!(
326                    "too many different paths: {}, out of {}, a few different paths can be expected \
327                        because of unambiguous shortest paths, but if there are too many something is \
328                        wrong",
329                    num_different_paths, NUM_QUERIES
330                );
331            }
332        }
333    }
334
335    fn gen_weighted_nodes(rng: &mut StdRng, max_node: usize, num: usize) -> Vec<(NodeId, Weight)> {
336        (0..num)
337            .map(|_| {
338                (
339                    rng.gen_range(0, max_node),
340                    // sometimes use max weight
341                    if rng.gen_range(0, 100) < 3 {
342                        WEIGHT_MAX
343                    } else {
344                        rng.gen_range(0, 100)
345                    },
346                )
347            })
348            .collect()
349    }
350
351    #[test]
352    fn save_to_and_load_from_disk() {
353        let mut g = InputGraph::new();
354        g.add_edge(0, 5, 6);
355        g.add_edge(5, 2, 1);
356        g.add_edge(2, 3, 4);
357        g.freeze();
358        let fast_graph = prepare(&g);
359        save_to_disk(&fast_graph, "example.fp").expect("writing to disk failed");
360        let loaded = load_from_disk("example.fp").unwrap();
361        remove_file("example.fp").expect("deleting file failed");
362        assert_eq!(fast_graph.get_num_nodes(), loaded.get_num_nodes());
363        assert_eq!(fast_graph.get_num_in_edges(), loaded.get_num_in_edges());
364        assert_eq!(fast_graph.get_num_out_edges(), loaded.get_num_out_edges());
365    }
366
367    #[test]
368    fn save_to_and_load_from_disk_32() {
369        let mut g = InputGraph::new();
370        g.add_edge(0, 5, 6);
371        g.add_edge(5, 2, 1);
372        g.add_edge(2, 3, 4);
373        g.freeze();
374        let fast_graph = prepare(&g);
375        save_to_disk32(&fast_graph, "example32.fp").expect("writing to disk failed");
376        let loaded = load_from_disk32("example32.fp").unwrap();
377        remove_file("example32.fp").expect("deleting file failed");
378        assert_eq!(fast_graph.get_num_nodes(), loaded.get_num_nodes());
379        assert_eq!(fast_graph.get_num_in_edges(), loaded.get_num_in_edges());
380        assert_eq!(fast_graph.get_num_out_edges(), loaded.get_num_out_edges());
381    }
382
383    #[test]
384    fn deterministic_result() {
385        const NUM_NODES: usize = 50;
386        const MEAN_DEGREE: f32 = 2.0;
387
388        // Repeat a few times to reduce test flakiness.
389        for _ in 0..10 {
390            let mut rng = create_rng();
391            let input_graph = InputGraph::random(&mut rng, NUM_NODES, MEAN_DEGREE);
392            let serialized1 = bincode::serialize(&prepare(&input_graph)).unwrap();
393            let serialized2 = bincode::serialize(&prepare(&input_graph)).unwrap();
394            if serialized1 != serialized2 {
395                panic!("Preparing and serializing the same graph twice produced different results");
396            }
397        }
398    }
399
400    #[ignore]
401    #[test]
402    fn run_performance_test_dist() {
403        println!("Running performance test for Bremen dist");
404        // road network extracted from OSM data from Bremen, Germany using the road distance as weight
405        // prep: 190ms, query: 16μs, out: 68494, in: 68426
406        // todo: try to tune parameters
407        run_performance_test(
408            &InputGraph::from_file("meta/test_maps/bremen_dist.gr"),
409            &Params::new(0.1, 500, 2, 50),
410            845493338,
411            30265,
412        )
413    }
414
415    #[ignore]
416    #[test]
417    fn run_performance_test_time() {
418        println!("Running performance test for Bremen time");
419        // road network extracted from OSM data from Bremen, Germany using the travel time as weight
420        // prep: 256ms, query: 11μs, out: 64825, in: 65027
421        // todo: try to tune parameters
422        run_performance_test(
423            &InputGraph::from_file("meta/test_maps/bremen_time.gr"),
424            &Params::new(0.1, 100, 2, 100),
425            88104267255,
426            30265,
427        );
428    }
429
430    #[ignore]
431    #[test]
432    fn run_performance_test_ballard() {
433        println!("Running performance test for ballard");
434        // prep: 1150ms, query: 53μs, out: 43849, in: 43700
435        // todo: try to tune parameters
436        run_performance_test(
437            &InputGraph::from_file("meta/test_maps/graph_ballard.gr"),
438            &Params::new(0.1, 100, 3, 100),
439            28409159409,
440            14992,
441        );
442    }
443
444    #[ignore]
445    #[test]
446    fn run_performance_test_23rd() {
447        println!("Running performance test for 23rd");
448        // prep: 170ms, query: 23μs, out: 11478, in: 11236
449        // todo: try to tune parameters
450        run_performance_test(
451            &InputGraph::from_file("meta/test_maps/graph_23rd.gr"),
452            &Params::new(0.1, 100, 3, 100),
453            19438403873,
454            20421,
455        );
456    }
457
458    #[ignore]
459    #[test]
460    fn run_performance_test_south_seattle_car() {
461        println!("Running performance test for South Seattle car");
462        // prep: 877ms, query: 26μs, out: 68777, in: 68161
463        // todo: try to tune parameters
464        run_performance_test(
465            &InputGraph::from_file("meta/test_maps/south_seattle_car.gr"),
466            &Params::new(0.1, 100, 10, 100),
467            77479396,
468            30805,
469        );
470    }
471
472    #[ignore]
473    #[test]
474    fn run_performance_test_bremen_dist_fixed_ordering() {
475        println!("Running performance test for Bremen dist (fixed node ordering)");
476        // prep: 340ms, prep_order: 64ms, query: 16μs, out: 66646, in: 66725
477        // todo: try to tune parameters
478        run_performance_test_fixed_ordering(
479            &InputGraph::from_file("meta/test_maps/bremen_dist.gr"),
480            &Params::default(),
481            &ParamsWithOrder::default(),
482            845493338,
483            30265,
484        );
485    }
486
487    #[ignore]
488    #[test]
489    fn run_performance_test_south_seattle_fixed_ordering() {
490        println!("Running performance test for South Seattle car (fixed node ordering)");
491        // prep: 811ms, prep order: 138ms, query: 27μs, out: 68777, in: 68161
492        // todo: try to tune parameters
493        run_performance_test_fixed_ordering(
494            &InputGraph::from_file("meta/test_maps/south_seattle_car.gr"),
495            &Params::new(0.1, 100, 10, 100),
496            &ParamsWithOrder::new(100),
497            77479396,
498            30805,
499        );
500    }
501
502    fn run_performance_test(
503        input_graph: &InputGraph,
504        params: &Params,
505        expected_checksum: usize,
506        expected_num_not_found: usize,
507    ) {
508        let mut fast_graph = FastGraph::new(1);
509        prepare_algo(
510            &mut |input_graph| fast_graph = prepare_with_params(input_graph, params),
511            &input_graph,
512        );
513        print_fast_graph_stats(&fast_graph);
514        let mut path_calculator = PathCalculator::new(fast_graph.get_num_nodes());
515        do_run_performance_test(
516            &mut |s, t| path_calculator.calc_path(&fast_graph, s, t),
517            input_graph.get_num_nodes(),
518            expected_checksum,
519            expected_num_not_found,
520        );
521    }
522
523    fn run_performance_test_fixed_ordering(
524        input_graph: &InputGraph,
525        params: &Params,
526        params_with_order: &ParamsWithOrder,
527        expected_checksum: usize,
528        expected_num_not_found: usize,
529    ) {
530        let mut time = Stopwatch::start_new();
531        let mut fast_graph = prepare_with_params(input_graph, params);
532        time.stop();
533        println!(
534            "preparation time (heuristic order). {} ms",
535            time.elapsed_ms()
536        );
537        let order = get_node_ordering(&fast_graph);
538        prepare_algo(
539            &mut |input_graph| {
540                fast_graph =
541                    prepare_with_order_with_params(input_graph, &order, params_with_order).unwrap()
542            },
543            &input_graph,
544        );
545        print_fast_graph_stats(&fast_graph);
546        let mut path_calculator = PathCalculator::new(fast_graph.get_num_nodes());
547        do_run_performance_test(
548            &mut |s, t| path_calculator.calc_path(&fast_graph, s, t),
549            input_graph.get_num_nodes(),
550            expected_checksum,
551            expected_num_not_found,
552        );
553    }
554
555    fn print_fast_graph_stats(fast_graph: &FastGraph) {
556        println!(
557            "number of nodes (fast graph) ...... {}",
558            fast_graph.get_num_nodes()
559        );
560        println!(
561            "number of out-edges (fast graph) .. {}",
562            fast_graph.get_num_out_edges()
563        );
564        println!(
565            "number of in-edges  (fast graph) .. {}",
566            fast_graph.get_num_in_edges()
567        );
568    }
569
570    pub fn prepare_algo<F>(preparation: &mut F, input_graph: &InputGraph)
571    where
572        F: FnMut(&InputGraph),
573    {
574        let mut time = Stopwatch::new();
575        time.start();
576        preparation(&input_graph);
577        time.stop();
578        println!(
579            "number of nodes (input graph) ..... {}",
580            input_graph.get_num_nodes()
581        );
582        println!(
583            "number of edges (input graph) ..... {}",
584            input_graph.get_num_edges()
585        );
586        println!(
587            "preparation time .................. {} ms",
588            time.elapsed_ms()
589        );
590    }
591
592    fn do_run_performance_test<F>(
593        calc_path: &mut F,
594        num_nodes: usize,
595        expected_checksum: usize,
596        expected_num_not_found: usize,
597    ) where
598        F: FnMut(NodeId, NodeId) -> Option<ShortestPath>,
599    {
600        let num_queries = 100_000;
601        let seed = 123;
602        let mut rng = create_rng_with_seed(seed);
603        let mut checksum = 0;
604        let mut num_not_found = 0;
605        let mut time = Stopwatch::new();
606        for _i in 0..num_queries {
607            let source = rng.gen_range(0, num_nodes);
608            let target = rng.gen_range(0, num_nodes);
609            time.start();
610            let path = calc_path(source, target);
611            time.stop();
612            match path {
613                Some(path) => checksum += path.get_weight(),
614                None => num_not_found += 1,
615            }
616        }
617        println!(
618            "total query time .................. {} ms",
619            time.elapsed_ms()
620        );
621        println!(
622            "query time on average ............. {} μs",
623            time.elapsed().as_micros() / (num_queries as u128)
624        );
625        assert_eq!(expected_checksum, checksum, "invalid checksum");
626        assert_eq!(
627            expected_num_not_found, num_not_found,
628            "invalid number of paths not found"
629        );
630    }
631
632    fn create_rng() -> StdRng {
633        let seed = create_seed();
634        create_rng_with_seed(seed)
635    }
636
637    fn create_rng_with_seed(seed: u64) -> StdRng {
638        debug!("creating random number generator with seed: {}", seed);
639        rand::SeedableRng::seed_from_u64(seed)
640    }
641
642    fn create_seed() -> u64 {
643        SystemTime::now()
644            .duration_since(SystemTime::UNIX_EPOCH)
645            .unwrap()
646            .as_nanos() as u64
647    }
648
649    /// Saves the given prepared graph to disk
650    fn save_to_disk(fast_graph: &FastGraph, file_name: &str) -> Result<(), Box<dyn Error>> {
651        let file = File::create(file_name)?;
652        Ok(bincode::serialize_into(file, fast_graph)?)
653    }
654
655    /// Restores a prepared graph from disk
656    fn load_from_disk(file_name: &str) -> Result<FastGraph, Box<dyn Error>> {
657        let file = File::open(file_name)?;
658        Ok(bincode::deserialize_from(file)?)
659    }
660
661    /// Saves the given prepared graph to disk thereby enforcing a 32bit representation no matter whether
662    /// the system in use uses 32 or 64bit. This is useful when creating the graph on a 64bit system and
663    /// afterwards loading it on a 32bit system.
664    /// Note: Using this method requires an extra +50% of RAM while storing the graph (even though
665    /// the graph will use 50% *less* disk space when it has been saved.
666    fn save_to_disk32(fast_graph: &FastGraph, file_name: &str) -> Result<(), Box<dyn Error>> {
667        let fast_graph32 = &FastGraph32::new(fast_graph);
668        let file = File::create(file_name)?;
669        Ok(bincode::serialize_into(file, fast_graph32)?)
670    }
671
672    /// Loads a graph from disk that was saved in 32bit representation, i.e. using save_to_disk32. The
673    /// graph will use usize to store integers, so most commonly either 32 or 64bits per integer
674    /// depending on the system in use.
675    /// Note: Using this method requires an extra +50% RAM while loading the graph.
676    fn load_from_disk32(file_name: &str) -> Result<FastGraph, Box<dyn Error>> {
677        let file = File::open(file_name)?;
678        let r: Result<FastGraph32, Box<dyn Error>> = Ok(bincode::deserialize_from(file)?);
679        r.map(|g| g.convert_to_usize())
680    }
681}