1use crate::implementation::{
2 debug_assert_graph_has_no_consecutive_dummy_edges, make_graph_eulerian_with_breaking_edges,
3 MatchtigEdgeData, PerformanceDataType, RelaxedAtomicBoolSlice, RelaxedAtomicBoolVec,
4 TARGET_DIJKSTRA_BLOCK_TIME,
5};
6use crate::{HeapType, NodeWeightArrayType, TigAlgorithm};
7use atomic_counter::{AtomicCounter, RelaxedCounter};
8use genome_graph::bigraph::algo::eulerian::{
9 compute_eulerian_superfluous_out_biedges,
10 compute_minimum_bidirected_eulerian_cycle_decomposition, decomposes_into_eulerian_bicycles,
11 find_non_eulerian_binodes_with_differences,
12};
13use genome_graph::bigraph::interface::dynamic_bigraph::DynamicEdgeCentricBigraph;
14use genome_graph::bigraph::interface::static_bigraph::StaticBigraph;
15use genome_graph::bigraph::interface::BidirectedData;
16use genome_graph::bigraph::traitgraph::index::{GraphIndex, OptionalGraphIndex};
17use genome_graph::bigraph::traitgraph::interface::GraphBase;
18use genome_graph::bigraph::traitgraph::walks::{EdgeWalk, VecEdgeWalk};
19use log::{error, info, warn};
20use std::collections::BinaryHeap;
21use std::marker::PhantomData;
22use std::ops::AddAssign;
23use std::sync::Mutex;
24use std::time::Instant;
25use traitgraph_algo::dijkstra::epoch_array_dijkstra_node_weight_array::EpochNodeWeightArray;
26use traitgraph_algo::dijkstra::performance_counters::{
27 DijkstraPerformanceCounter, DijkstraPerformanceData, NoopDijkstraPerformanceCounter,
28};
29use traitgraph_algo::dijkstra::{
30 Dijkstra, DijkstraExhaustiveness, DijkstraHeap, DijkstraWeightedEdgeData, NodeWeightArray,
31};
32
33#[derive(Default)]
35pub struct GreedytigAlgorithm<SequenceHandle> {
36 _phantom_data: PhantomData<SequenceHandle>,
37}
38
39pub struct GreedytigAlgorithmConfiguration {
41 pub threads: usize,
43 pub k: usize,
45 pub staged_parallelism_divisor: Option<f64>,
48 pub resource_limit_factor: usize,
50 pub node_weight_array_type: NodeWeightArrayType,
52 pub heap_type: HeapType,
54 pub performance_data_type: PerformanceDataType,
56}
57
58impl GreedytigAlgorithmConfiguration {
59 #[allow(dead_code)]
62 pub fn new(threads: usize, k: usize) -> Self {
63 Self {
64 threads,
65 k,
66 staged_parallelism_divisor: None,
67 resource_limit_factor: 0,
68 node_weight_array_type: NodeWeightArrayType::HashbrownHashMap,
69 heap_type: HeapType::StdBinaryHeap,
70 performance_data_type: PerformanceDataType::None,
71 }
72 }
73}
74
75impl<Graph: GraphBase, SequenceHandle: Default + Clone> TigAlgorithm<Graph>
76 for GreedytigAlgorithm<SequenceHandle>
77where
78 Graph: DynamicEdgeCentricBigraph + Send + Sync,
79 Graph::NodeIndex: Send + Sync,
80 Graph::EdgeData: BidirectedData + Eq + Clone + MatchtigEdgeData<SequenceHandle>,
81{
82 type Configuration = GreedytigAlgorithmConfiguration;
83
84 fn compute_tigs(
85 graph: &mut Graph,
86 configuration: &Self::Configuration,
87 ) -> Vec<VecEdgeWalk<Graph>> {
88 compute_greedytigs_choose_heap_type(graph, configuration)
89 }
90}
91
92fn compute_greedytigs_choose_heap_type<
93 NodeIndex: GraphIndex<OptionalNodeIndex> + Send + Sync,
94 OptionalNodeIndex: OptionalGraphIndex<NodeIndex>,
95 SequenceHandle: Default + Clone,
96 EdgeData: BidirectedData + Eq + MatchtigEdgeData<SequenceHandle> + Clone,
97 Graph: DynamicEdgeCentricBigraph<
98 NodeIndex = NodeIndex,
99 OptionalNodeIndex = OptionalNodeIndex,
100 EdgeData = EdgeData,
101 > + Send
102 + Sync,
103>(
104 graph: &mut Graph,
105 configuration: &GreedytigAlgorithmConfiguration,
106) -> Vec<VecEdgeWalk<Graph>> {
107 match configuration.heap_type {
108 HeapType::StdBinaryHeap => {
109 compute_greedytigs_choose_node_weight_array_type::<_, _, _, _, _, BinaryHeap<_>>(
110 graph,
111 configuration,
112 )
113 }
114 }
115}
116
117fn compute_greedytigs_choose_node_weight_array_type<
118 NodeIndex: GraphIndex<OptionalNodeIndex> + Send + Sync,
119 OptionalNodeIndex: OptionalGraphIndex<NodeIndex>,
120 SequenceHandle: Default + Clone,
121 EdgeData: BidirectedData + Eq + MatchtigEdgeData<SequenceHandle> + Clone,
122 Graph: DynamicEdgeCentricBigraph<
123 NodeIndex = NodeIndex,
124 OptionalNodeIndex = OptionalNodeIndex,
125 EdgeData = EdgeData,
126 > + Send
127 + Sync,
128 DijkstraHeapType: DijkstraHeap<usize, Graph::NodeIndex>,
129>(
130 graph: &mut Graph,
131 configuration: &GreedytigAlgorithmConfiguration,
132) -> Vec<VecEdgeWalk<Graph>> {
133 match configuration.node_weight_array_type {
134 NodeWeightArrayType::EpochNodeWeightArray => {
135 compute_greedytigs_choose_dijkstra_performance_type::<
136 _,
137 _,
138 _,
139 _,
140 _,
141 DijkstraHeapType,
142 EpochNodeWeightArray<_>,
143 >(graph, configuration)
144 }
145 NodeWeightArrayType::HashbrownHashMap => {
146 compute_greedytigs_choose_dijkstra_performance_type::<
147 _,
148 _,
149 _,
150 _,
151 _,
152 DijkstraHeapType,
153 hashbrown::HashMap<_, _>,
154 >(graph, configuration)
155 }
156 }
157}
158
159fn compute_greedytigs_choose_dijkstra_performance_type<
160 NodeIndex: GraphIndex<OptionalNodeIndex> + Send + Sync,
161 OptionalNodeIndex: OptionalGraphIndex<NodeIndex>,
162 SequenceHandle: Default + Clone,
163 EdgeData: BidirectedData + Eq + MatchtigEdgeData<SequenceHandle> + Clone,
164 Graph: DynamicEdgeCentricBigraph<
165 NodeIndex = NodeIndex,
166 OptionalNodeIndex = OptionalNodeIndex,
167 EdgeData = EdgeData,
168 > + Send
169 + Sync,
170 DijkstraHeapType: DijkstraHeap<usize, Graph::NodeIndex>,
171 DijkstraNodeWeightArray: NodeWeightArray<usize>,
172>(
173 graph: &mut Graph,
174 configuration: &GreedytigAlgorithmConfiguration,
175) -> Vec<VecEdgeWalk<Graph>> {
176 match configuration.performance_data_type {
177 PerformanceDataType::None => compute_greedytigs::<
178 _,
179 _,
180 _,
181 _,
182 _,
183 DijkstraHeapType,
184 DijkstraNodeWeightArray,
185 NoopDijkstraPerformanceCounter,
186 >(graph, configuration),
187 PerformanceDataType::Complete => compute_greedytigs::<
188 _,
189 _,
190 _,
191 _,
192 _,
193 DijkstraHeapType,
194 DijkstraNodeWeightArray,
195 DijkstraPerformanceCounter,
196 >(graph, configuration),
197 }
198}
199
200fn compute_greedytigs<
202 NodeIndex: GraphIndex<OptionalNodeIndex> + Send + Sync,
203 OptionalNodeIndex: OptionalGraphIndex<NodeIndex>,
204 SequenceHandle: Default + Clone,
205 EdgeData: BidirectedData + Eq + MatchtigEdgeData<SequenceHandle> + Clone,
206 Graph: DynamicEdgeCentricBigraph<
207 NodeIndex = NodeIndex,
208 OptionalNodeIndex = OptionalNodeIndex,
209 EdgeData = EdgeData,
210 > + Send
211 + Sync,
212 DijkstraHeapType: DijkstraHeap<usize, Graph::NodeIndex>,
213 DijkstraNodeWeightArray: NodeWeightArray<usize>,
214 DijkstraPerformance: DijkstraPerformanceData + Default + AddAssign + Send,
215>(
216 graph: &mut Graph,
217 configuration: &GreedytigAlgorithmConfiguration,
218) -> Vec<VecEdgeWalk<Graph>> {
219 let threads = configuration.threads;
220 let k = configuration.k;
221
222 info!("Collecting nodes with missing incoming or outgoing edges");
223 let mut out_nodes = Vec::new(); let mut in_node_count = 0;
225 let in_node_map = RelaxedAtomicBoolVec::new(graph.node_count());
226 let mut node_multiplicities = vec![0; graph.node_count()];
227 let mut unbalanced_self_mirror_count = 0;
228
229 for node_index in graph.node_indices() {
230 let diff = compute_eulerian_superfluous_out_biedges(graph, node_index);
231 if graph.is_self_mirror_node(node_index) && diff != 0 {
232 in_node_count += 1;
233 in_node_map.set(node_index.as_usize(), true);
234 node_multiplicities[node_index.as_usize()] = diff;
235 out_nodes.push(node_index);
236 unbalanced_self_mirror_count += 1;
237 } else if diff > 0 {
238 in_node_count += 1;
239 in_node_map.set(node_index.as_usize(), true);
240 node_multiplicities[node_index.as_usize()] = diff;
241 } else if diff < 0 {
242 out_nodes.push(node_index);
243 node_multiplicities[node_index.as_usize()] = diff;
244 }
245 }
246
247 info!(
248 "Found {} nodes with missing outgoing edges",
249 out_nodes.len()
250 );
251 info!("Found {} nodes with missing incoming edges", in_node_count);
252 info!(
253 "Of those there are {} self-mirrors",
254 unbalanced_self_mirror_count
255 );
256
257 info!("Computing shortest paths between nodes with missing outgoing and nodes with missing incoming edges");
258
259 let dummy_sequence = SequenceHandle::default();
260 let mut dummy_edge_id = {
261 info!(
262 "Using {} threads to run ~{} dijkstras",
263 threads,
264 out_nodes.len()
265 );
266 let start = Instant::now();
267
268 let locked_node_multiplicities: Vec<_> = node_multiplicities
269 .iter()
270 .copied()
271 .map(Mutex::new)
272 .collect();
273 let executed_dijkstras = RelaxedCounter::default();
274
275 #[allow(clippy::too_many_arguments)]
276 fn compute_dijkstras<
277 EdgeData: DijkstraWeightedEdgeData<usize>,
278 Graph: StaticBigraph<EdgeData = EdgeData>,
279 DijkstraNodeWeightArray: NodeWeightArray<usize>,
280 DijkstraHeapType: DijkstraHeap<usize, Graph::NodeIndex>,
281 DijkstraPerformance: DijkstraPerformanceData + Default,
282 >(
283 graph: &Graph,
284 dijkstra: &mut Dijkstra<Graph, usize, DijkstraNodeWeightArray, DijkstraHeapType>,
285 distances: &mut Vec<(Graph::NodeIndex, usize)>,
286 shortest_paths: &mut Vec<(Graph::NodeIndex, Graph::NodeIndex, usize)>,
287 out_nodes: &[Graph::NodeIndex],
288 missed_nodes: RelaxedAtomicBoolSlice,
289 dijkstra_performance_limit: usize,
290 in_node_map: &RelaxedAtomicBoolVec,
291 locked_node_multiplicities: &[Mutex<isize>],
292 k: usize,
293 executed_dijkstras: &RelaxedCounter,
294 output: bool,
295 output_step: usize,
296 total_dijkstras: usize,
297 ) -> DijkstraPerformance {
298 let mut last_output = 0;
299 let mut dijkstra_performance = DijkstraPerformance::default();
300
301 for (i, &out_node) in out_nodes.iter().enumerate() {
302 let out_node_is_self_mirror = graph.is_self_mirror_node(out_node);
303 let out_node_mirror = graph.mirror_node(out_node).unwrap();
304 let out_node_multiplicity_lock = locked_node_multiplicities
307 [out_node_mirror.as_usize()]
308 .lock()
309 .unwrap();
310 let mut out_node_multiplicity: isize = *out_node_multiplicity_lock;
311 drop(out_node_multiplicity_lock);
312
313 debug_assert!(
314 (0..=4).contains(&out_node_multiplicity),
315 "out_node_multiplicity = {out_node_multiplicity}, out_node_is_self_mirror = {out_node_is_self_mirror}"
316 );
317
318 if out_node_multiplicity == 0 {
319 continue;
320 }
321
322 while out_node_multiplicity > 0 {
323 let target_amount = (out_node_multiplicity + 1).try_into().unwrap();
324 let dijkstra_status = dijkstra.shortest_path_lens(
325 graph,
326 out_node,
327 in_node_map,
328 target_amount,
329 k - 1,
330 true,
331 distances,
332 dijkstra_performance_limit,
333 dijkstra_performance_limit,
334 dijkstra_performance,
335 );
336 dijkstra_performance = dijkstra_status.performance_data;
337
338 if distances.is_empty() {
339 missed_nodes.set(
340 i,
341 dijkstra_status.exhaustiveness != DijkstraExhaustiveness::Complete,
342 );
343
344 break;
346 }
347
348 let abort_after_this = distances.len() < target_amount;
349
350 for &(in_node, distance) in distances.iter() {
351 let mut is_self_mirror_edge = false;
352 if in_node == out_node_mirror {
353 if out_node_multiplicity < 2 {
354 continue;
355 } else {
356 is_self_mirror_edge = true;
357 }
358 };
359 let is_self_mirror_edge = is_self_mirror_edge;
360 debug_assert!(!is_self_mirror_edge || !out_node_is_self_mirror);
361
362 let in_node_mirror = graph.mirror_node(in_node).unwrap();
363 let in_node_is_self_mirror = graph.is_self_mirror_node(in_node);
364 debug_assert!(!is_self_mirror_edge || !in_node_is_self_mirror);
365
366 let mut lock_indices = vec![out_node.as_usize()];
367 if !out_node_is_self_mirror {
368 lock_indices.push(out_node_mirror.as_usize());
369 }
370 if !is_self_mirror_edge {
371 lock_indices.push(in_node.as_usize());
372 if !in_node_is_self_mirror {
373 lock_indices.push(in_node_mirror.as_usize());
374 }
375 }
376 let lock_indices = lock_indices;
377
378 let lock_permutation = permutation::sort(lock_indices.as_slice());
379 let mut locks = vec![None, None, None, None];
380
381 for lock_rank in 0..lock_indices.len() {
382 let lock_index = lock_permutation.apply_inv_idx(lock_rank);
383
384 locks[lock_index] = Some(
387 locked_node_multiplicities[lock_indices[lock_index]]
388 .lock()
389 .unwrap(),
390 );
391 }
392
393 let mut locks: Vec<_> = locks
394 .into_iter()
395 .take(lock_indices.len())
396 .map(|o| o.unwrap())
397 .collect();
398 let in_node_lock_offset = if out_node_is_self_mirror { 1 } else { 2 };
399 let multiplicity_reduction = if is_self_mirror_edge { 2 } else { 1 };
400
401 if out_node_is_self_mirror {
402 debug_assert!(*locks[0] >= 0);
403 debug_assert!(*locks[0] <= 1);
404 out_node_multiplicity = *locks[0];
405 } else {
406 debug_assert!(*locks[0] <= 0);
407 debug_assert!(*locks[0] >= -4);
408 debug_assert_eq!(*locks[0], -*locks[1]);
409 out_node_multiplicity = -*locks[0];
410 }
411
412 if out_node_multiplicity == 0 {
413 break;
414 }
415
416 if !is_self_mirror_edge {
417 if in_node_is_self_mirror {
418 debug_assert!(*locks[in_node_lock_offset] >= 0,
419 "*locks[in_node_lock_offset] = {}, in_node_lock_offset = {}, out_node_is_self_mirror = {}, in_node_is_self_mirror = {}, is_self_mirror_edge = {}, lock_indices = {:?}, lock_permutation = {:?}, locks = {:?}",
420 *locks[in_node_lock_offset],
421 in_node_lock_offset,
422 out_node_is_self_mirror,
423 in_node_is_self_mirror,
424 is_self_mirror_edge,
425 lock_indices,
426 lock_permutation,
427 locks.iter().map(|l| **l).collect::<Vec<_>>());
428 debug_assert!(*locks[in_node_lock_offset] <= 1,
429 "*locks[in_node_lock_offset] = {}, in_node_lock_offset = {}, out_node_is_self_mirror = {}, in_node_is_self_mirror = {}, is_self_mirror_edge = {}, lock_indices = {:?}, lock_permutation = {:?}, locks = {:?}",
430 *locks[in_node_lock_offset],
431 in_node_lock_offset,
432 out_node_is_self_mirror,
433 in_node_is_self_mirror,
434 is_self_mirror_edge,
435 lock_indices,
436 lock_permutation,
437 locks.iter().map(|l| **l).collect::<Vec<_>>());
438 } else {
439 debug_assert!(*locks[in_node_lock_offset] >= 0,
440 "*locks[in_node_lock_offset] = {}, in_node_lock_offset = {}, in_node_is_self_mirror = {}, is_self_mirror_edge = {}, lock_permutation = {:?}, locks = {:?}",
441 *locks[in_node_lock_offset],
442 in_node_lock_offset,
443 in_node_is_self_mirror,
444 is_self_mirror_edge,
445 lock_permutation,
446 locks.iter().map(|l| **l).collect::<Vec<_>>());
447 debug_assert!(*locks[in_node_lock_offset] <= 4);
448 debug_assert_eq!(
449 *locks[in_node_lock_offset],
450 -*locks[in_node_lock_offset + 1]
451 );
452 }
453
454 let in_node_multiplicity = *locks[in_node_lock_offset];
455 if in_node_multiplicity == 0 {
456 in_node_map.set(in_node.as_usize(), false);
457 continue;
458 }
459 }
460
461 shortest_paths.push((out_node, in_node, distance));
462
463 if out_node_is_self_mirror {
464 *locks[0] -= 1;
465 debug_assert!(*locks[0] >= 0);
466 debug_assert!(*locks[0] <= 1);
467 } else {
468 *locks[0] += multiplicity_reduction;
469 *locks[1] -= multiplicity_reduction;
470 debug_assert!(*locks[0] <= 0);
471 debug_assert!(*locks[0] >= -4);
472 debug_assert_eq!(*locks[0], -*locks[1]);
473 }
474 out_node_multiplicity = -(*locks[0]);
475
476 if !is_self_mirror_edge {
477 *locks[in_node_lock_offset] -= 1;
478
479 if in_node_is_self_mirror {
480 debug_assert!(*locks[in_node_lock_offset] >= 0);
481 debug_assert!(*locks[in_node_lock_offset] <= 1);
482 } else {
483 *locks[in_node_lock_offset + 1] += 1;
484 debug_assert!(*locks[in_node_lock_offset] >= 0);
485 debug_assert!(*locks[in_node_lock_offset] <= 4);
486 debug_assert_eq!(
487 *locks[in_node_lock_offset],
488 -*locks[in_node_lock_offset + 1]
489 );
490 }
491 }
492
493 if out_node_multiplicity == 0 {
494 in_node_map.set(out_node_mirror.as_usize(), false);
495 }
496
497 if !is_self_mirror_edge {
498 if *locks[in_node_lock_offset] == 0 {
499 in_node_map.set(in_node.as_usize(), false);
500 }
501 }
502 }
503
504 if abort_after_this {
505 missed_nodes.set(
506 i,
507 dijkstra_status.exhaustiveness != DijkstraExhaustiveness::Complete,
508 );
509
510 break;
511 }
512 }
513
514 executed_dijkstras.inc();
515 if output && executed_dijkstras.get() - last_output > output_step {
516 last_output += output_step;
517 println!(
518 "{}%, ~{} total shortest paths",
519 last_output * 100 / total_dijkstras,
520 (shortest_paths.len() as f64 * total_dijkstras as f64 / i as f64) as u64,
521 );
522 }
523 }
524
525 dijkstra_performance
526 }
527
528 let dijkstra_start = Instant::now();
529
530 let results = Mutex::new(Vec::new());
531 let shared_graph = &*graph;
532 let mut dijkstra_performance = DijkstraPerformance::default();
533
534 let mut unbalanced_out_nodes = out_nodes;
535 let mut missed_nodes = RelaxedAtomicBoolVec::new(unbalanced_out_nodes.len());
536
537 for iteration in 0.. {
538 let threads = if iteration == 0 {
539 threads
540 } else {
541 (((threads as f64)
542 / configuration
543 .staged_parallelism_divisor
544 .expect("Iteration > 0 will never be reached if this is None")
545 .powi(iteration)) as usize)
546 .max(1)
547 };
548 let resource_limit =
549 if threads == 1 || configuration.staged_parallelism_divisor.is_none() {
550 usize::MAX
551 } else {
552 graph
553 .node_count()
554 .saturating_mul(configuration.resource_limit_factor)
555 / threads
556 };
557 let offset = Mutex::new(0);
558
559 crossbeam::scope(|scope| {
560 info!("Starting {threads} dijkstra threads with a resource limit of {resource_limit} each");
561 let mut thread_handles = Vec::new();
562
563 for _ in 0..threads {
564 thread_handles.push(scope.spawn(|_| {
565 let graph = shared_graph;
566 let mut dijkstra =
567 Dijkstra::<_, _, DijkstraNodeWeightArray, DijkstraHeapType>::new(graph);
568 let mut distances = Vec::new();
569 let mut shortest_paths = Vec::new();
570 let mut chunk_size = 1024;
571 let mut dijkstra_performance = DijkstraPerformance::default();
572
573 loop {
574 let (current_offset, current_limit) = {
575 let mut offset = offset.lock().unwrap();
576 let current_offset = *offset;
577 let remaining_nodes = unbalanced_out_nodes.len() - *offset;
578
579 if remaining_nodes == 0 {
580 break;
581 }
582
583 chunk_size = chunk_size
584 .min(remaining_nodes / threads)
585 .max(10)
586 .max(chunk_size / 10)
587 .min(remaining_nodes);
588 let current_limit = current_offset + chunk_size;
589 *offset = current_limit;
590 (current_offset, current_limit)
591 };
592
593 let dijkstra_start = Instant::now();
594 dijkstra_performance += compute_dijkstras(
595 graph,
596 &mut dijkstra,
597 &mut distances,
598 &mut shortest_paths,
599 &unbalanced_out_nodes[current_offset..current_limit],
600 missed_nodes.slice(current_offset..current_limit),
601 resource_limit,
602 &in_node_map,
603 &locked_node_multiplicities,
604 k,
605 &executed_dijkstras,
606 false,
607 unbalanced_out_nodes.len() / 100,
608 unbalanced_out_nodes.len(),
609 );
610 let dijkstra_end = Instant::now();
611
612 let duration = (dijkstra_end - dijkstra_start).as_secs_f32();
613 chunk_size = ((chunk_size as f32) * (TARGET_DIJKSTRA_BLOCK_TIME / duration))
614 as usize;
615 chunk_size = chunk_size.max(10);
616 }
617
618 results.lock().unwrap().append(&mut shortest_paths);
619 dijkstra_performance
620 }));
621 }
622
623 info!("Waiting for {} dijkstra threads to finish", thread_handles.len());
624 for thread_handle in thread_handles {
625 dijkstra_performance += thread_handle.join().unwrap();
626 }
627 }).unwrap();
628
629 unbalanced_out_nodes = unbalanced_out_nodes
630 .iter()
631 .zip(missed_nodes.iter())
632 .filter_map(|(&out_node, missed)| if missed { Some(out_node) } else { None })
633 .collect();
634 missed_nodes.reinitialise(unbalanced_out_nodes.len());
635 info!(
636 "{} remaining unbalanced out nodes",
637 unbalanced_out_nodes.len()
638 );
639
640 if unbalanced_out_nodes.is_empty() {
641 break;
642 }
643 assert!(configuration.staged_parallelism_divisor.is_some(), "Have nodes where Dijkstra missed part of the search space, but not in staged parallelism mode");
644 }
645
646 let results = results.into_inner().unwrap();
647 if let (Some(unnecessary_heap_elements), Some(iterations)) = (
648 dijkstra_performance.unnecessary_heap_elements(),
649 dijkstra_performance.iterations(),
650 ) {
651 info!(
652 "Dijkstras had a factor of {:.3} unnecessary heap elements",
653 unnecessary_heap_elements as f64 / iterations as f64
654 );
655 }
656 if let Some(max_max_heap_size) = dijkstra_performance.max_max_heap_size() {
657 info!("Dijktras had a maximum maximum heap size of {max_max_heap_size}");
658 }
659 if let Some(max_max_distance_array_size) =
660 dijkstra_performance.max_max_distance_array_size()
661 {
662 info!("Dijktras had a maximum maximum distance array size of {max_max_distance_array_size}");
663 }
664 if let Some(average_max_heap_size) = dijkstra_performance.average_max_heap_size() {
665 info!("Dijktras had an average maximum heap size of {average_max_heap_size:.0}");
666 }
667 if let Some(average_max_distance_array_size) =
668 dijkstra_performance.average_max_distance_array_size()
669 {
670 info!(
671 "Dijktras had an average maximum heap size of {average_max_distance_array_size:.0}"
672 );
673 }
674
675 info!("Found {} shortest paths", results.len());
676 let dijkstra_time = (Instant::now() - dijkstra_start).as_nanos();
677
678 let mut dummy_edge_id = 0;
679 for (out_node, in_node, distance) in results {
680 let sequence = dummy_sequence.clone();
681 dummy_edge_id += 1;
682 let edge_data = EdgeData::new(sequence, true, distance, dummy_edge_id);
683 graph.add_edge(out_node, in_node, edge_data.clone());
684 graph.add_edge(
685 graph.mirror_node(in_node).unwrap(),
686 graph.mirror_node(out_node).unwrap(),
687 edge_data.mirror(),
688 );
689 }
690
691 let end = Instant::now();
692 info!(
693 "Took {:.6}s for computing paths and getting edges, of this {:.6}s are from dijkstra",
694 (end - start).as_secs_f64(),
695 dijkstra_time as f64 / 1e9
696 );
697 dummy_edge_id
698 };
699
700 debug_assert!(graph.verify_node_pairing());
701 debug_assert!(graph.verify_edge_mirror_property());
702 debug_assert_graph_has_no_consecutive_dummy_edges(graph, k);
703
704 info!("Making graph Eulerian by adding breaking dummy edges");
705 make_graph_eulerian_with_breaking_edges(graph, dummy_sequence, &mut dummy_edge_id, k);
706
707 if !decomposes_into_eulerian_bicycles(graph) {
709 let non_eulerian_nodes_and_differences = find_non_eulerian_binodes_with_differences(graph);
710 error!(
711 "Failed to make the graph Eulerian. Non-Eulerian nodes and differences:\n{:?}",
712 non_eulerian_nodes_and_differences
713 );
714 panic!("Failed to make the graph Eulerian.");
715 }
716 debug_assert!(graph.verify_node_pairing());
717 debug_assert!(graph.verify_edge_mirror_property());
718 debug_assert_graph_has_no_consecutive_dummy_edges(graph, k);
719
720 info!("Finding Eulerian bicycle");
721 let mut eulerian_cycles = compute_minimum_bidirected_eulerian_cycle_decomposition(graph);
723 info!("Found {} Eulerian bicycles", eulerian_cycles.len());
724
725 info!("Breaking Eulerian bicycles at expensive temporary edges");
726 let mut greedytigs = Vec::new();
727
728 let mut removed_edges = 0;
729 for eulerian_cycle in &mut eulerian_cycles {
730 info!(
731 "Processing Eulerian bicycle with {} biedges",
732 eulerian_cycle.len()
733 );
734 debug_assert!(eulerian_cycle.is_circular_walk(graph));
735
736 let mut longest_dummy_weight = 0;
738 let mut longest_dummy_index = 0;
739 for (index, &edge) in eulerian_cycle.iter().enumerate() {
740 let edge_data = graph.edge_data(edge);
741 if edge_data.is_dummy() && edge_data.weight() > longest_dummy_weight {
742 longest_dummy_weight = edge_data.weight();
743 longest_dummy_index = index;
744 }
745 }
746 if longest_dummy_weight > 0 {
747 eulerian_cycle.rotate_left(longest_dummy_index);
748 }
749
750 let mut offset = 0;
751 let mut last_edge_is_dummy = false;
752 for (current_cycle_index, ¤t_edge_index) in eulerian_cycle.iter().enumerate() {
753 let edge_data = graph.edge_data(current_edge_index);
754
755 if edge_data.is_original() {
756 last_edge_is_dummy = false;
757 } else {
758 if last_edge_is_dummy {
759 warn!(
760 "Found consecutive dummy edges at {}",
761 current_edge_index.as_usize()
762 );
763 }
764 last_edge_is_dummy = true;
765 }
766
767 if (edge_data.weight() >= k && edge_data.is_dummy())
768 || (edge_data.is_dummy() && current_cycle_index == 0)
769 {
770 if offset < current_cycle_index {
771 greedytigs.push(eulerian_cycle[offset..current_cycle_index].to_owned());
772 } else if current_cycle_index > 0 {
773 warn!("Found consecutive breaking edges");
774 }
775 offset = current_cycle_index + 1;
776 removed_edges += 1;
777 }
778 }
779 if offset < eulerian_cycle.len() {
780 if graph
781 .edge_data(*eulerian_cycle.last().unwrap())
782 .is_original()
783 {
784 greedytigs.push(eulerian_cycle[offset..eulerian_cycle.len()].to_owned());
785 } else if offset < eulerian_cycle.len() - 1 {
786 greedytigs.push(eulerian_cycle[offset..eulerian_cycle.len() - 1].to_owned());
787 }
788 }
789 }
790
791 info!("Found {} expensive temporary edges", removed_edges);
792 info!("Found {} greedytigs", greedytigs.len());
793
794 for greedytig in &greedytigs {
795 debug_assert!(!greedytig.is_empty());
796 debug_assert!(graph.edge_data(*greedytig.first().unwrap()).is_original());
797 debug_assert!(graph.edge_data(*greedytig.last().unwrap()).is_original());
798 }
799
800 greedytigs
801}