1#[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
53pub fn prepare(input_graph: &InputGraph) -> FastGraph {
55 FastGraphBuilder::build(input_graph)
56}
57
58pub fn prepare_with_params(input_graph: &InputGraph, params: &Params) -> FastGraph {
60 FastGraphBuilder::build_with_params(input_graph, params)
61}
62
63pub fn prepare_with_order(input_graph: &InputGraph, order: &[NodeId]) -> Result<FastGraph, String> {
68 FastGraphBuilder::build_with_order(input_graph, order)
69}
70
71pub 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
80pub 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
86pub 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
101pub fn create_calculator(fast_graph: &FastGraph) -> PathCalculator {
105 PathCalculator::new(fast_graph.get_num_nodes())
106}
107
108pub fn get_node_ordering(fast_graph: &FastGraph) -> Vec<NodeId> {
111 fast_graph.get_node_ordering()
112}
113
114pub fn serialize_32<S: Serializer>(fg: &FastGraph, s: S) -> Result<S::Ok, S::Error> {
119 FastGraph32::new(fg).serialize(s)
120}
121
122pub 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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}