1use std::cmp::Ordering;
39use std::collections::HashMap;
40use std::collections::VecDeque;
41#[cfg(feature = "copious-debugging")]
42use std::fmt::Display;
43
44use itertools::izip;
45use itertools::Itertools;
46
47use crate::arena::Arena;
48use crate::arena::Handle;
49use crate::arena::HandleSet;
50use crate::arena::List;
51use crate::arena::ListArena;
52use crate::arena::ListCell;
53use crate::arena::SupplementalArena;
54use crate::cycles::Appendables;
55use crate::cycles::AppendingCycleDetector;
56use crate::cycles::SimilarPathDetector;
57use crate::cycles::SimilarPathStats;
58use crate::graph::Degree;
59use crate::graph::Edge;
60use crate::graph::File;
61use crate::graph::Node;
62use crate::graph::StackGraph;
63use crate::graph::Symbol;
64use crate::partial::Cyclicity;
65use crate::partial::PartialPath;
66use crate::partial::PartialPaths;
67use crate::partial::PartialSymbolStack;
68use crate::paths::Extend;
69use crate::paths::PathResolutionError;
70use crate::stats::FrequencyDistribution;
71use crate::CancellationError;
72use crate::CancellationFlag;
73
74pub trait Appendable {
79 fn append_to(
82 &self,
83 graph: &StackGraph,
84 partials: &mut PartialPaths,
85 path: &mut PartialPath,
86 ) -> Result<(), PathResolutionError>;
87
88 fn start_node(&self) -> Handle<Node>;
90
91 fn end_node(&self) -> Handle<Node>;
93
94 fn display<'a>(
96 &'a self,
97 graph: &'a StackGraph,
98 partials: &'a mut PartialPaths,
99 ) -> Box<dyn std::fmt::Display + 'a>;
100}
101
102impl Appendable for Edge {
103 fn append_to(
104 &self,
105 graph: &StackGraph,
106 partials: &mut PartialPaths,
107 path: &mut PartialPath,
108 ) -> Result<(), PathResolutionError> {
109 path.resolve_to_node(graph, partials, self.source)?;
110 path.append(graph, partials, *self)
111 }
112
113 fn start_node(&self) -> Handle<Node> {
114 self.source
115 }
116
117 fn end_node(&self) -> Handle<Node> {
118 self.sink
119 }
120
121 fn display<'a>(
122 &'a self,
123 graph: &'a StackGraph,
124 _partials: &'a mut PartialPaths,
125 ) -> Box<dyn std::fmt::Display + 'a> {
126 Box::new(format!(
127 "{} -> {}",
128 self.source.display(graph),
129 self.sink.display(graph)
130 ))
131 }
132}
133
134impl Appendable for PartialPath {
135 fn append_to(
136 &self,
137 graph: &StackGraph,
138 partials: &mut PartialPaths,
139 path: &mut PartialPath,
140 ) -> Result<(), PathResolutionError> {
141 path.resolve_to_node(graph, partials, self.start_node)?;
142 path.ensure_no_overlapping_variables(partials, self);
143 path.concatenate(graph, partials, self)?;
144 Ok(())
145 }
146
147 fn start_node(&self) -> Handle<Node> {
148 self.start_node
149 }
150
151 fn end_node(&self) -> Handle<Node> {
152 self.end_node
153 }
154
155 fn display<'a>(
156 &'a self,
157 graph: &'a StackGraph,
158 partials: &'a mut PartialPaths,
159 ) -> Box<dyn std::fmt::Display + 'a> {
160 Box::new(self.display(graph, partials))
161 }
162}
163
164pub trait ToAppendable<H, A>
174where
175 A: Appendable,
176{
177 fn get_appendable<'a>(&'a self, handle: &'a H) -> &'a A;
178}
179
180pub trait ForwardCandidates<H, A, Db, Err>
187where
188 A: Appendable,
189 Db: ToAppendable<H, A>,
190{
191 fn load_forward_candidates(
194 &mut self,
195 _path: &PartialPath,
196 _cancellation_flag: &dyn CancellationFlag,
197 ) -> Result<(), Err> {
198 Ok(())
199 }
200
201 fn get_forward_candidates<R>(&mut self, path: &PartialPath, result: &mut R)
205 where
206 R: std::iter::Extend<H>;
207
208 fn get_joining_candidate_degree(&self, path: &PartialPath) -> Degree;
210
211 fn get_graph_partials_and_db(&mut self) -> (&StackGraph, &mut PartialPaths, &Db);
213}
214
215pub struct GraphEdgeCandidates<'a> {
220 graph: &'a StackGraph,
221 partials: &'a mut PartialPaths,
222 file: Option<Handle<File>>,
223 edges: GraphEdges,
224}
225
226impl<'a> GraphEdgeCandidates<'a> {
227 pub fn new(
228 graph: &'a StackGraph,
229 partials: &'a mut PartialPaths,
230 file: Option<Handle<File>>,
231 ) -> Self {
232 Self {
233 graph,
234 partials,
235 file,
236 edges: GraphEdges,
237 }
238 }
239}
240
241impl ForwardCandidates<Edge, Edge, GraphEdges, CancellationError> for GraphEdgeCandidates<'_> {
242 fn get_forward_candidates<R>(&mut self, path: &PartialPath, result: &mut R)
243 where
244 R: std::iter::Extend<Edge>,
245 {
246 result.extend(self.graph.outgoing_edges(path.end_node).filter(|e| {
247 self.file
248 .map_or(true, |file| self.graph[e.sink].is_in_file(file))
249 }));
250 }
251
252 fn get_joining_candidate_degree(&self, path: &PartialPath) -> Degree {
253 self.graph.incoming_edge_degree(path.end_node)
254 }
255
256 fn get_graph_partials_and_db(&mut self) -> (&StackGraph, &mut PartialPaths, &GraphEdges) {
257 (self.graph, self.partials, &self.edges)
258 }
259}
260
261pub struct GraphEdges;
264
265impl ToAppendable<Edge, Edge> for GraphEdges {
266 fn get_appendable<'a>(&'a self, edge: &'a Edge) -> &'a Edge {
267 edge
268 }
269}
270
271pub struct Database {
284 pub(crate) partial_paths: Arena<PartialPath>,
285 pub(crate) local_nodes: HandleSet<Node>,
286 symbol_stack_keys: ListArena<Handle<Symbol>>,
287 symbol_stack_key_cache: HashMap<SymbolStackCacheKey, SymbolStackKeyHandle>,
288 paths_by_start_node: SupplementalArena<Node, Vec<Handle<PartialPath>>>,
289 root_paths_by_precondition_prefix:
290 SupplementalArena<SymbolStackKeyCell, Vec<Handle<PartialPath>>>,
291 root_paths_by_precondition_with_variable:
292 SupplementalArena<SymbolStackKeyCell, Vec<Handle<PartialPath>>>,
293 root_paths_by_precondition_without_variable:
294 SupplementalArena<SymbolStackKeyCell, Vec<Handle<PartialPath>>>,
295 incoming_paths: SupplementalArena<Node, Degree>,
296}
297
298impl Database {
299 pub fn new() -> Database {
301 Database {
302 partial_paths: Arena::new(),
303 local_nodes: HandleSet::new(),
304 symbol_stack_keys: List::new_arena(),
305 symbol_stack_key_cache: HashMap::new(),
306 paths_by_start_node: SupplementalArena::new(),
307 root_paths_by_precondition_prefix: SupplementalArena::new(),
308 root_paths_by_precondition_with_variable: SupplementalArena::new(),
309 root_paths_by_precondition_without_variable: SupplementalArena::new(),
310 incoming_paths: SupplementalArena::new(),
311 }
312 }
313
314 #[cfg_attr(not(feature = "storage"), allow(dead_code))]
317 pub(crate) fn clear(&mut self) {
318 self.partial_paths.clear();
319 self.local_nodes.clear();
320 self.symbol_stack_keys.clear();
321 self.symbol_stack_key_cache.clear();
322 self.paths_by_start_node.clear();
323 self.root_paths_by_precondition_prefix.clear();
324 self.root_paths_by_precondition_with_variable.clear();
325 self.root_paths_by_precondition_without_variable.clear();
326 self.incoming_paths.clear();
327 }
328
329 pub fn add_partial_path(
332 &mut self,
333 graph: &StackGraph,
334 partials: &mut PartialPaths,
335 path: PartialPath,
336 ) -> Handle<PartialPath> {
337 let start_node = path.start_node;
338 let end_node = path.end_node;
339 copious_debugging!(
340 " Add {} path to database {}",
341 if graph[start_node].is_root() {
342 "root"
343 } else {
344 "node"
345 },
346 path.display(graph, partials)
347 );
348 let symbol_stack_precondition = path.symbol_stack_precondition;
349 let handle = self.partial_paths.add(path);
350
351 if graph[start_node].is_root() {
353 let mut key = SymbolStackKey::from_partial_symbol_stack(
356 partials,
357 self,
358 symbol_stack_precondition,
359 );
360 if !key.is_empty() {
361 match symbol_stack_precondition.has_variable() {
362 true => self.root_paths_by_precondition_with_variable[key.back_handle()]
363 .push(handle),
364 false => self.root_paths_by_precondition_without_variable[key.back_handle()]
365 .push(handle),
366 }
367 }
368 while key.pop_back(self).is_some() && !key.is_empty() {
369 self.root_paths_by_precondition_prefix[key.back_handle()].push(handle);
370 }
371 } else {
372 self.paths_by_start_node[start_node].push(handle);
374 }
375
376 self.incoming_paths[end_node] += Degree::One;
377 handle
378 }
379
380 pub fn find_candidate_partial_paths<R>(
384 &mut self,
385 graph: &StackGraph,
386 partials: &mut PartialPaths,
387 path: &PartialPath,
388 result: &mut R,
389 ) where
390 R: std::iter::Extend<Handle<PartialPath>>,
391 {
392 if graph[path.end_node].is_root() {
393 self.find_candidate_partial_paths_from_root(
396 graph,
397 partials,
398 Some(path.symbol_stack_postcondition),
399 result,
400 );
401 } else {
402 self.find_candidate_partial_paths_from_node(graph, partials, path.end_node, result);
403 }
404 }
405
406 #[cfg_attr(not(feature = "copious-debugging"), allow(unused_variables))]
409 pub fn find_candidate_partial_paths_from_root<R>(
410 &mut self,
411 graph: &StackGraph,
412 partials: &mut PartialPaths,
413 symbol_stack: Option<PartialSymbolStack>,
414 result: &mut R,
415 ) where
416 R: std::iter::Extend<Handle<PartialPath>>,
417 {
418 match symbol_stack {
421 Some(symbol_stack) => {
422 let mut key =
423 SymbolStackKey::from_partial_symbol_stack(partials, self, symbol_stack);
424 copious_debugging!(
425 " Search for symbol stack <{}>",
426 key.display(graph, self)
427 );
428 if let Some(paths) = self
430 .root_paths_by_precondition_without_variable
431 .get(key.back_handle())
432 {
433 #[cfg(feature = "copious-debugging")]
434 {
435 for path in paths {
436 copious_debugging!(
437 " Found path with exact stack {}",
438 self[*path].display(graph, partials)
439 );
440 }
441 }
442 result.extend(paths.iter().copied());
443 }
444 if symbol_stack.has_variable() {
446 if let Some(paths) = self
447 .root_paths_by_precondition_prefix
448 .get(key.back_handle())
449 {
450 #[cfg(feature = "copious-debugging")]
451 {
452 for path in paths {
453 copious_debugging!(
454 " Found path with smaller stack {}",
455 self[*path].display(graph, partials)
456 );
457 }
458 }
459 result.extend(paths.iter().copied());
460 }
461 }
462 loop {
463 if let Some(paths) = self
465 .root_paths_by_precondition_with_variable
466 .get(key.back_handle())
467 {
468 #[cfg(feature = "copious-debugging")]
469 {
470 for path in paths {
471 copious_debugging!(
472 " Found path with smaller stack {}",
473 self[*path].display(graph, partials)
474 );
475 }
476 }
477 result.extend(paths.iter().copied());
478 }
479 if key.pop_back(self).is_none() {
480 break;
481 }
482 }
483 }
484 None => {
485 copious_debugging!(" Search for all root paths");
486 for (_, paths) in self
487 .root_paths_by_precondition_with_variable
488 .iter()
489 .chain(self.root_paths_by_precondition_without_variable.iter())
490 {
491 #[cfg(feature = "copious-debugging")]
492 {
493 for path in paths {
494 copious_debugging!(
495 " Found path {}",
496 self[*path].display(graph, partials)
497 );
498 }
499 }
500 result.extend(paths.iter().copied());
501 }
502 }
503 }
504 }
505
506 #[cfg_attr(not(feature = "copious-debugging"), allow(unused_variables))]
511 pub fn find_candidate_partial_paths_from_node<R>(
512 &self,
513 graph: &StackGraph,
514 partials: &mut PartialPaths,
515 start_node: Handle<Node>,
516 result: &mut R,
517 ) where
518 R: std::iter::Extend<Handle<PartialPath>>,
519 {
520 copious_debugging!(" Search for start node {}", start_node.display(graph));
521 if let Some(paths) = self.paths_by_start_node.get(start_node) {
523 #[cfg(feature = "copious-debugging")]
524 {
525 for path in paths {
526 copious_debugging!(
527 " Found path {}",
528 self[*path].display(graph, partials)
529 );
530 }
531 }
532 result.extend(paths.iter().copied());
533 }
534 }
535
536 pub fn get_incoming_path_degree(&self, end_node: Handle<Node>) -> Degree {
538 self.incoming_paths[end_node]
539 }
540
541 pub fn find_local_nodes(&mut self) {
550 self.local_nodes.clear();
553 for handle in self.iter_partial_paths() {
554 self.local_nodes.add(self[handle].start_node);
555 self.local_nodes.add(self[handle].end_node);
556 }
557
558 let mut nonlocal_start_nodes = HandleSet::new();
560 let mut nonlocal_end_nodes = HandleSet::new();
561 self.local_nodes.remove(StackGraph::root_node());
562 nonlocal_start_nodes.add(StackGraph::root_node());
563 nonlocal_end_nodes.add(StackGraph::root_node());
564 self.local_nodes.remove(StackGraph::jump_to_node());
565 nonlocal_start_nodes.add(StackGraph::jump_to_node());
566 nonlocal_end_nodes.add(StackGraph::jump_to_node());
567
568 let mut keep_checking = true;
571 while keep_checking {
572 keep_checking = false;
573 for handle in self.iter_partial_paths() {
574 let start_node = self[handle].start_node;
575 let end_node = self[handle].end_node;
576
577 let start_node_is_nonlocal = nonlocal_start_nodes.contains(start_node);
580 let end_node_is_nonlocal = nonlocal_start_nodes.contains(end_node);
581 if start_node_is_nonlocal && !end_node_is_nonlocal {
582 keep_checking = true;
583 nonlocal_start_nodes.add(end_node);
584 self.local_nodes.remove(end_node);
585 }
586
587 let start_node_is_nonlocal = nonlocal_end_nodes.contains(start_node);
590 let end_node_is_nonlocal = nonlocal_end_nodes.contains(end_node);
591 if !start_node_is_nonlocal && end_node_is_nonlocal {
592 keep_checking = true;
593 nonlocal_end_nodes.add(start_node);
594 self.local_nodes.remove(start_node);
595 }
596 }
597 }
598 }
599
600 pub fn mark_local_node(&mut self, node: Handle<Node>) {
607 self.local_nodes.add(node);
608 }
609
610 pub fn node_is_local(&self, node: Handle<Node>) -> bool {
614 self.local_nodes.contains(node)
615 }
616
617 pub fn iter_partial_paths(&self) -> impl Iterator<Item = Handle<PartialPath>> {
621 self.partial_paths.iter_handles()
622 }
623
624 pub fn ensure_both_directions(&mut self, partials: &mut PartialPaths) {
625 for path in self.partial_paths.iter_handles() {
626 self.partial_paths
627 .get_mut(path)
628 .ensure_both_directions(partials);
629 }
630 }
631
632 pub fn ensure_forwards(&mut self, partials: &mut PartialPaths) {
633 for path in self.partial_paths.iter_handles() {
634 self.partial_paths.get_mut(path).ensure_forwards(partials);
635 }
636 }
637}
638
639impl std::ops::Index<Handle<PartialPath>> for Database {
640 type Output = PartialPath;
641 #[inline(always)]
642 fn index(&self, handle: Handle<PartialPath>) -> &PartialPath {
643 self.partial_paths.get(handle)
644 }
645}
646
647impl ToAppendable<Handle<PartialPath>, PartialPath> for Database {
648 fn get_appendable<'a>(&'a self, handle: &'a Handle<PartialPath>) -> &'a PartialPath {
649 &self[*handle]
650 }
651}
652
653pub struct DatabaseCandidates<'a> {
654 graph: &'a StackGraph,
655 partials: &'a mut PartialPaths,
656 database: &'a mut Database,
657}
658
659impl<'a> DatabaseCandidates<'a> {
660 pub fn new(
661 graph: &'a StackGraph,
662 partials: &'a mut PartialPaths,
663 database: &'a mut Database,
664 ) -> Self {
665 Self {
666 graph,
667 partials,
668 database,
669 }
670 }
671}
672
673impl ForwardCandidates<Handle<PartialPath>, PartialPath, Database, CancellationError>
674 for DatabaseCandidates<'_>
675{
676 fn get_forward_candidates<R>(&mut self, path: &PartialPath, result: &mut R)
677 where
678 R: std::iter::Extend<Handle<PartialPath>>,
679 {
680 self.database
681 .find_candidate_partial_paths(self.graph, self.partials, path, result);
682 }
683
684 fn get_joining_candidate_degree(&self, path: &PartialPath) -> Degree {
685 self.database.get_incoming_path_degree(path.end_node)
686 }
687
688 fn get_graph_partials_and_db(&mut self) -> (&StackGraph, &mut PartialPaths, &Database) {
689 (self.graph, self.partials, self.database)
690 }
691}
692
693#[derive(Clone, Copy)]
696pub struct SymbolStackKey {
697 symbols: List<Handle<Symbol>>,
702}
703
704#[derive(Clone, Eq, Hash, PartialEq)]
705struct SymbolStackCacheKey {
706 head: Handle<Symbol>,
707 tail: SymbolStackKeyHandle,
708}
709
710type SymbolStackKeyCell = ListCell<Handle<Symbol>>;
711type SymbolStackKeyHandle = Handle<SymbolStackKeyCell>;
712
713impl SymbolStackKey {
714 fn empty() -> SymbolStackKey {
716 SymbolStackKey {
717 symbols: List::empty(),
718 }
719 }
720
721 fn is_empty(&self) -> bool {
722 self.symbols.is_empty()
723 }
724
725 fn push_back(&mut self, db: &mut Database, symbol: Handle<Symbol>) {
727 let cache_key = SymbolStackCacheKey {
728 head: symbol,
729 tail: self.back_handle(),
730 };
731 if let Some(handle) = db.symbol_stack_key_cache.get(&cache_key) {
732 self.symbols = List::from_handle(*handle);
733 return;
734 }
735 self.symbols.push_front(&mut db.symbol_stack_keys, symbol);
737 let handle = self.back_handle();
738 db.symbol_stack_key_cache.insert(cache_key, handle);
739 }
740
741 fn pop_back(&mut self, db: &Database) -> Option<Handle<Symbol>> {
743 self.symbols.pop_front(&db.symbol_stack_keys).copied()
745 }
746
747 pub fn from_partial_symbol_stack(
749 partials: &mut PartialPaths,
750 db: &mut Database,
751 mut stack: PartialSymbolStack,
752 ) -> SymbolStackKey {
753 let mut result = SymbolStackKey::empty();
754 while let Some(symbol) = stack.pop_front(partials) {
755 result.push_back(db, symbol.symbol);
756 }
757 result
758 }
759
760 fn back_handle(self) -> SymbolStackKeyHandle {
762 self.symbols.handle()
765 }
766
767 #[cfg(feature = "copious-debugging")]
768 fn display<'a>(self, graph: &'a StackGraph, db: &'a Database) -> impl Display + 'a {
769 DisplaySymbolStackKey(self, graph, db)
770 }
771}
772
773#[cfg(feature = "copious-debugging")]
774struct DisplaySymbolStackKey<'a>(SymbolStackKey, &'a StackGraph, &'a Database);
775
776#[cfg(feature = "copious-debugging")]
777impl<'a> Display for DisplaySymbolStackKey<'a> {
778 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
779 fn display_one(
781 mut key: SymbolStackKey,
782 graph: &StackGraph,
783 db: &Database,
784 f: &mut std::fmt::Formatter,
785 ) -> std::fmt::Result {
786 let last = match key.pop_back(db) {
787 Some(last) => last,
788 None => return Ok(()),
789 };
790 display_one(key, graph, db, f)?;
791 last.display(graph).fmt(f)
792 }
793 display_one(self.0, self.1, self.2, f)
794 }
795}
796
797pub struct ForwardPartialPathStitcher<H> {
827 candidates: Vec<H>,
828 extensions: Vec<(PartialPath, AppendingCycleDetector<H>)>,
829 queue: VecDeque<(PartialPath, AppendingCycleDetector<H>, bool)>,
830 initial_paths_in_queue: usize,
833 next_iteration: (
836 VecDeque<PartialPath>,
837 VecDeque<AppendingCycleDetector<H>>,
838 VecDeque<bool>,
839 ),
840 appended_paths: Appendables<H>,
841 similar_path_detector: Option<SimilarPathDetector<PartialPath>>,
842 check_only_join_nodes: bool,
843 max_work_per_phase: usize,
844 initial_paths: usize,
845 stats: Option<Stats>,
846 #[cfg(feature = "copious-debugging")]
847 phase_number: usize,
848}
849
850impl<H> ForwardPartialPathStitcher<H> {
851 pub fn from_partial_paths<I>(
855 _graph: &StackGraph,
856 _partials: &mut PartialPaths,
857 initial_partial_paths: I,
858 ) -> Self
859 where
860 I: IntoIterator<Item = PartialPath>,
861 {
862 let mut appended_paths = Appendables::new();
863 let next_iteration: (VecDeque<_>, VecDeque<_>, VecDeque<_>) = initial_partial_paths
864 .into_iter()
865 .map(|p| {
866 let c = AppendingCycleDetector::from(&mut appended_paths, p.clone().into());
867 (p, c, false)
868 })
869 .multiunzip();
870 let initial_paths = next_iteration.0.len();
871 Self {
872 candidates: Vec::new(),
873 extensions: Vec::new(),
874 queue: VecDeque::new(),
875 initial_paths_in_queue: initial_paths,
876 next_iteration,
877 appended_paths,
878 similar_path_detector: Some(SimilarPathDetector::new()),
880 check_only_join_nodes: false,
882 max_work_per_phase: usize::MAX,
884 initial_paths,
885 stats: None,
886 #[cfg(feature = "copious-debugging")]
887 phase_number: 1,
888 }
889 }
890
891 pub fn set_similar_path_detection(&mut self, detect_similar_paths: bool) {
896 if !detect_similar_paths {
897 self.similar_path_detector = None;
898 } else if self.similar_path_detector.is_none() {
899 let mut similar_path_detector = SimilarPathDetector::new();
900 similar_path_detector.set_collect_stats(self.stats.is_some());
901 self.similar_path_detector = Some(similar_path_detector);
902 }
903 }
904
905 pub fn set_check_only_join_nodes(&mut self, check_only_join_nodes: bool) {
910 self.check_only_join_nodes = check_only_join_nodes;
911 }
912
913 pub fn set_max_work_per_phase(&mut self, max_work_per_phase: usize) {
919 self.max_work_per_phase = max_work_per_phase;
920 }
921
922 pub fn set_collect_stats(&mut self, collect_stats: bool) {
924 if !collect_stats {
925 self.stats = None;
926 } else if self.stats.is_none() {
927 let mut stats = Stats::default();
928 stats.initial_paths.record(self.initial_paths);
929 self.stats = Some(stats);
930 }
931 if let Some(similar_path_detector) = &mut self.similar_path_detector {
932 similar_path_detector.set_collect_stats(collect_stats);
933 }
934 }
935
936 pub fn into_stats(mut self) -> Stats {
937 if let (Some(stats), Some(similar_path_detector)) =
938 (&mut self.stats, self.similar_path_detector)
939 {
940 stats.similar_paths_stats = similar_path_detector.stats();
941 }
942 self.stats.unwrap_or_default()
943 }
944}
945
946impl<H: Clone> ForwardPartialPathStitcher<H> {
947 pub fn previous_phase_partial_paths(&self) -> impl Iterator<Item = &PartialPath> + '_ {
950 self.next_iteration.0.iter()
951 }
952
953 pub fn previous_phase_partial_paths_slice(&mut self) -> &[PartialPath] {
956 self.next_iteration.0.make_contiguous();
957 self.next_iteration.0.as_slices().0
958 }
959
960 pub fn previous_phase_partial_paths_slice_mut(&mut self) -> &mut [PartialPath] {
963 self.next_iteration.0.make_contiguous();
964 self.next_iteration.0.as_mut_slices().0
965 }
966
967 fn extend<A, Db, C, Err>(
971 &mut self,
972 candidates: &mut C,
973 partial_path: &PartialPath,
974 cycle_detector: AppendingCycleDetector<H>,
975 has_split: bool,
976 ) -> usize
977 where
978 A: Appendable,
979 Db: ToAppendable<H, A>,
980 C: ForwardCandidates<H, A, Db, Err>,
981 {
982 let check_cycle = !self.check_only_join_nodes
983 || partial_path.start_node == partial_path.end_node
984 || candidates.get_joining_candidate_degree(partial_path) == Degree::Multiple;
985
986 let (graph, partials, db) = candidates.get_graph_partials_and_db();
987 copious_debugging!(" Extend {}", partial_path.display(graph, partials));
988
989 if check_cycle {
990 let has_precondition_variables = partial_path.symbol_stack_precondition.has_variable()
993 || partial_path.scope_stack_precondition.has_variable();
994 let cycles = cycle_detector
995 .is_cyclic(graph, partials, db, &mut self.appended_paths)
996 .expect("cyclic test failed when stitching partial paths");
997 let cyclic = match has_precondition_variables {
998 false => !cycles
1002 .into_iter()
1003 .all(|c| c == Cyclicity::StrengthensPrecondition),
1004 true => !cycles.is_empty(),
1009 };
1010 if cyclic {
1011 copious_debugging!(" is discontinued: cyclic");
1012 return 0;
1013 }
1014 }
1015
1016 self.candidates.clear();
1018 candidates.get_forward_candidates(partial_path, &mut self.candidates);
1019 let (graph, partials, db) = candidates.get_graph_partials_and_db();
1020
1021 let candidate_count = self.candidates.len();
1023 self.extensions.clear();
1024 self.extensions.reserve(candidate_count);
1025 for candidate in &self.candidates {
1026 let appendable = db.get_appendable(candidate);
1027 copious_debugging!(" with {}", appendable.display(graph, partials));
1028
1029 let mut new_partial_path = partial_path.clone();
1030 let mut new_cycle_detector = cycle_detector.clone();
1031 #[cfg_attr(not(feature = "copious-debugging"), allow(unused_variables))]
1034 {
1035 if let Err(err) = appendable.append_to(graph, partials, &mut new_partial_path) {
1036 copious_debugging!(" is invalid: {:?}", err);
1037 continue;
1038 }
1039 }
1040 new_cycle_detector.append(&mut self.appended_paths, candidate.clone());
1041 copious_debugging!(" is {}", new_partial_path.display(graph, partials));
1042 self.extensions.push((new_partial_path, new_cycle_detector));
1043 }
1044
1045 let extension_count = self.extensions.len();
1046 let new_has_split = has_split || self.extensions.len() > 1;
1047 self.next_iteration.0.reserve(extension_count);
1048 self.next_iteration.1.reserve(extension_count);
1049 self.next_iteration.2.reserve(extension_count);
1050 for (new_partial_path, new_cycle_detector) in self.extensions.drain(..) {
1051 let check_similar_path = new_has_split
1052 && (!self.check_only_join_nodes
1053 || candidates.get_joining_candidate_degree(&new_partial_path)
1054 == Degree::Multiple);
1055 let (graph, partials, _) = candidates.get_graph_partials_and_db();
1056 if check_similar_path {
1057 if let Some(similar_path_detector) = &mut self.similar_path_detector {
1058 if similar_path_detector.add_path(
1059 graph,
1060 partials,
1061 &new_partial_path,
1062 |ps, left, right| {
1063 if !left.equals(ps, right) {
1064 None
1065 } else {
1066 if left.shadows(ps, right) {
1067 Some(Ordering::Less)
1068 } else if right.shadows(ps, left) {
1069 Some(Ordering::Greater)
1070 } else {
1071 Some(Ordering::Equal)
1072 }
1073 }
1074 },
1075 ) {
1076 copious_debugging!(
1077 " extension {}",
1078 new_partial_path.display(graph, partials)
1079 );
1080 copious_debugging!(" is rejected: too many similar");
1081 continue;
1082 }
1083 }
1084 }
1085
1086 self.next_iteration.0.push(new_partial_path);
1087 self.next_iteration.1.push(new_cycle_detector);
1088 self.next_iteration.2.push(new_has_split);
1089 }
1090
1091 if let Some(stats) = &mut self.stats {
1092 let (graph, _, _) = candidates.get_graph_partials_and_db();
1093 let end_node = &graph[partial_path.end_node];
1094 if end_node.is_root() {
1095 stats.candidates_per_root_path.record(candidate_count);
1096 stats.extensions_per_root_path.record(extension_count);
1097 stats.root_visits += 1;
1098 } else {
1099 stats.candidates_per_node_path.record(candidate_count);
1100 stats.extensions_per_node_path.record(extension_count);
1101 stats.node_visits.record(end_node.id());
1102 }
1103 if extension_count == 0 {
1104 stats.terminal_path_lengh.record(partial_path.edges.len());
1105 }
1106 }
1107 candidate_count
1108 }
1109
1110 pub fn is_complete(&self) -> bool {
1112 self.queue.is_empty() && self.next_iteration.0.is_empty()
1113 }
1114
1115 pub fn process_next_phase<A, Db, C, E, Err>(&mut self, candidates: &mut C, extend_while: E)
1128 where
1129 A: Appendable,
1130 Db: ToAppendable<H, A>,
1131 C: ForwardCandidates<H, A, Db, Err>,
1132 E: Fn(&StackGraph, &mut PartialPaths, &PartialPath) -> bool,
1133 {
1134 copious_debugging!("==> Start phase {}", self.phase_number);
1135 self.queue.extend(izip!(
1136 self.next_iteration.0.drain(..),
1137 self.next_iteration.1.drain(..),
1138 self.next_iteration.2.drain(..),
1139 ));
1140 if let Some(stats) = &mut self.stats {
1141 stats.queued_paths_per_phase.record(self.queue.len());
1142 }
1143 let mut work_performed = 0;
1144 while let Some((partial_path, cycle_detector, has_split)) = self.queue.pop_front() {
1145 let (graph, partials, _) = candidates.get_graph_partials_and_db();
1146 copious_debugging!(
1147 "--> Candidate partial path {}",
1148 partial_path.display(graph, partials)
1149 );
1150 if self.initial_paths_in_queue > 0 {
1151 self.initial_paths_in_queue -= 1;
1152 } else if !extend_while(graph, partials, &partial_path) {
1153 copious_debugging!(
1154 " Do not extend {}",
1155 partial_path.display(graph, partials)
1156 );
1157 continue;
1158 }
1159 work_performed += self.extend(candidates, &partial_path, cycle_detector, has_split);
1160 if work_performed >= self.max_work_per_phase {
1161 break;
1162 }
1163 }
1164 if let Some(stats) = &mut self.stats {
1165 stats.processed_paths_per_phase.record(work_performed);
1166 }
1167
1168 #[cfg(feature = "copious-debugging")]
1169 {
1170 if let Some(similar_path_detector) = &self.similar_path_detector {
1171 copious_debugging!(
1172 " Max similar path bucket size: {}",
1173 similar_path_detector.max_bucket_size()
1174 );
1175 }
1176 copious_debugging!("==> End phase {}", self.phase_number);
1177 self.phase_number += 1;
1178 }
1179 }
1180}
1181
1182impl ForwardPartialPathStitcher<Edge> {
1183 pub fn find_minimal_partial_path_set_in_file<F>(
1200 graph: &StackGraph,
1201 partials: &mut PartialPaths,
1202 file: Handle<File>,
1203 config: StitcherConfig,
1204 cancellation_flag: &dyn CancellationFlag,
1205 mut visit: F,
1206 ) -> Result<Stats, CancellationError>
1207 where
1208 F: FnMut(&StackGraph, &mut PartialPaths, &PartialPath),
1209 {
1210 fn as_complete_as_necessary(graph: &StackGraph, path: &PartialPath) -> bool {
1211 path.starts_at_endpoint(graph)
1212 && (path.ends_at_endpoint(graph) || path.ends_in_jump(graph))
1213 }
1214
1215 let initial_paths = graph
1216 .nodes_for_file(file)
1217 .chain(std::iter::once(StackGraph::root_node()))
1218 .filter(|node| graph[*node].is_endpoint())
1219 .map(|node| PartialPath::from_node(graph, partials, node))
1220 .collect::<Vec<_>>();
1221 let mut stitcher =
1222 ForwardPartialPathStitcher::from_partial_paths(graph, partials, initial_paths);
1223 config.apply(&mut stitcher);
1224 stitcher.set_check_only_join_nodes(true);
1225
1226 let mut accepted_path_length = FrequencyDistribution::default();
1227 while !stitcher.is_complete() {
1228 cancellation_flag.check("finding complete partial paths")?;
1229 stitcher.process_next_phase(
1230 &mut GraphEdgeCandidates::new(graph, partials, Some(file)),
1231 |g, _ps, p| !as_complete_as_necessary(g, p),
1232 );
1233 for path in stitcher.previous_phase_partial_paths() {
1234 if as_complete_as_necessary(graph, path) {
1235 accepted_path_length.record(path.edges.len());
1236 visit(graph, partials, path);
1237 }
1238 }
1239 }
1240
1241 Ok(Stats {
1242 accepted_path_length,
1243 ..stitcher.into_stats()
1244 })
1245 }
1246}
1247
1248impl<H: Clone> ForwardPartialPathStitcher<H> {
1249 pub fn find_all_complete_partial_paths<I, F, A, Db, C, Err>(
1261 candidates: &mut C,
1262 starting_nodes: I,
1263 config: StitcherConfig,
1264 cancellation_flag: &dyn CancellationFlag,
1265 mut visit: F,
1266 ) -> Result<Stats, Err>
1267 where
1268 I: IntoIterator<Item = Handle<Node>>,
1269 A: Appendable,
1270 Db: ToAppendable<H, A>,
1271 C: ForwardCandidates<H, A, Db, Err>,
1272 F: FnMut(&StackGraph, &mut PartialPaths, &PartialPath),
1273 Err: std::convert::From<CancellationError>,
1274 {
1275 let (graph, partials, _) = candidates.get_graph_partials_and_db();
1276 let initial_paths = starting_nodes
1277 .into_iter()
1278 .filter(|n| graph[*n].is_reference())
1279 .map(|n| {
1280 let mut p = PartialPath::from_node(graph, partials, n);
1281 p.eliminate_precondition_stack_variables(partials);
1282 p
1283 })
1284 .collect::<Vec<_>>();
1285 let mut stitcher =
1286 ForwardPartialPathStitcher::from_partial_paths(graph, partials, initial_paths);
1287 config.apply(&mut stitcher);
1288 stitcher.set_check_only_join_nodes(true);
1289
1290 let mut accepted_path_length = FrequencyDistribution::default();
1291 while !stitcher.is_complete() {
1292 cancellation_flag.check("finding complete partial paths")?;
1293 for path in stitcher.previous_phase_partial_paths() {
1294 candidates.load_forward_candidates(path, cancellation_flag)?;
1295 }
1296 stitcher.process_next_phase(candidates, |_, _, _| true);
1297 let (graph, partials, _) = candidates.get_graph_partials_and_db();
1298 for path in stitcher.previous_phase_partial_paths() {
1299 if path.is_complete(graph) {
1300 accepted_path_length.record(path.edges.len());
1301 visit(graph, partials, path);
1302 }
1303 }
1304 }
1305
1306 Ok(Stats {
1307 accepted_path_length,
1308 ..stitcher.into_stats()
1309 })
1310 }
1311}
1312
1313#[derive(Clone, Debug, Default)]
1314pub struct Stats {
1315 pub initial_paths: FrequencyDistribution<usize>,
1317 pub queued_paths_per_phase: FrequencyDistribution<usize>,
1319 pub processed_paths_per_phase: FrequencyDistribution<usize>,
1321 pub accepted_path_length: FrequencyDistribution<usize>,
1323 pub terminal_path_lengh: FrequencyDistribution<usize>,
1325 pub candidates_per_node_path: FrequencyDistribution<usize>,
1327 pub candidates_per_root_path: FrequencyDistribution<usize>,
1329 pub extensions_per_node_path: FrequencyDistribution<usize>,
1331 pub extensions_per_root_path: FrequencyDistribution<usize>,
1333 pub root_visits: usize,
1335 pub node_visits: FrequencyDistribution<crate::graph::NodeID>,
1337 pub similar_paths_stats: SimilarPathStats,
1339}
1340
1341impl std::ops::AddAssign<Self> for Stats {
1342 fn add_assign(&mut self, rhs: Self) {
1343 self.initial_paths += rhs.initial_paths;
1344 self.queued_paths_per_phase += rhs.queued_paths_per_phase;
1345 self.processed_paths_per_phase += rhs.processed_paths_per_phase;
1346 self.accepted_path_length += rhs.accepted_path_length;
1347 self.terminal_path_lengh += rhs.terminal_path_lengh;
1348 self.candidates_per_node_path += rhs.candidates_per_node_path;
1349 self.candidates_per_root_path += rhs.candidates_per_root_path;
1350 self.extensions_per_node_path += rhs.extensions_per_node_path;
1351 self.extensions_per_root_path += rhs.extensions_per_root_path;
1352 self.root_visits += rhs.root_visits;
1353 self.node_visits += rhs.node_visits;
1354 self.similar_paths_stats += rhs.similar_paths_stats;
1355 }
1356}
1357
1358impl std::ops::AddAssign<&Self> for Stats {
1359 fn add_assign(&mut self, rhs: &Self) {
1360 self.initial_paths += &rhs.initial_paths;
1361 self.processed_paths_per_phase += &rhs.processed_paths_per_phase;
1362 self.accepted_path_length += &rhs.accepted_path_length;
1363 self.terminal_path_lengh += &rhs.terminal_path_lengh;
1364 self.candidates_per_node_path += &rhs.candidates_per_node_path;
1365 self.candidates_per_root_path += &rhs.candidates_per_root_path;
1366 self.extensions_per_node_path += &rhs.extensions_per_node_path;
1367 self.extensions_per_root_path += &rhs.extensions_per_root_path;
1368 self.root_visits += rhs.root_visits;
1369 self.node_visits += &rhs.node_visits;
1370 self.similar_paths_stats += &rhs.similar_paths_stats;
1371 }
1372}
1373
1374#[derive(Clone, Copy, Debug)]
1376pub struct StitcherConfig {
1377 detect_similar_paths: bool,
1379 collect_stats: bool,
1381}
1382
1383impl StitcherConfig {
1384 pub fn detect_similar_paths(&self) -> bool {
1385 self.detect_similar_paths
1386 }
1387
1388 pub fn with_detect_similar_paths(mut self, detect_similar_paths: bool) -> Self {
1389 self.detect_similar_paths = detect_similar_paths;
1390 self
1391 }
1392
1393 pub fn collect_stats(&self) -> bool {
1394 self.collect_stats
1395 }
1396
1397 pub fn with_collect_stats(mut self, collect_stats: bool) -> Self {
1398 self.collect_stats = collect_stats;
1399 self
1400 }
1401}
1402
1403impl StitcherConfig {
1404 fn apply<H>(&self, stitcher: &mut ForwardPartialPathStitcher<H>) {
1405 stitcher.set_similar_path_detection(self.detect_similar_paths);
1406 stitcher.set_collect_stats(self.collect_stats);
1407 }
1408}
1409
1410impl Default for StitcherConfig {
1411 fn default() -> Self {
1412 Self {
1413 detect_similar_paths: true,
1414 collect_stats: false,
1415 }
1416 }
1417}