Skip to content
This repository was archived by the owner on Sep 9, 2025. It is now read-only.

Commit 1e8d06b

Browse files
author
Hendrik van Antwerpen
committed
Use new cycle detection in Paths as well
1 parent 3fc760a commit 1e8d06b

File tree

2 files changed

+84
-27
lines changed

2 files changed

+84
-27
lines changed

stack-graphs/src/paths.rs

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,16 @@ use crate::arena::DequeArena;
2626
use crate::arena::Handle;
2727
use crate::arena::List;
2828
use crate::arena::ListArena;
29+
use crate::cycles::Appendages;
30+
use crate::cycles::AppendingCycleDetector;
2931
use crate::cycles::SimilarPathDetector;
3032
use crate::graph::Edge;
3133
use crate::graph::Node;
3234
use crate::graph::NodeID;
3335
use crate::graph::StackGraph;
3436
use crate::graph::Symbol;
37+
use crate::partial::Cyclicity;
38+
use crate::partial::PartialPaths;
3539
use crate::utils::cmp_option;
3640
use crate::utils::equals_option;
3741
use crate::CancellationError;
@@ -867,17 +871,26 @@ impl Path {
867871
/// parameter, instead of building it up ourselves, so that you have control over which
868872
/// particular collection type to use, and so that you can reuse result collections across
869873
/// multiple calls.
870-
pub fn extend<R: Extend<Path>>(&self, graph: &StackGraph, paths: &mut Paths, result: &mut R) {
874+
pub fn extend<R: Extend<(Path, AppendingCycleDetector<Edge>)>>(
875+
&self,
876+
graph: &StackGraph,
877+
paths: &mut Paths,
878+
edges: &mut Appendages<Edge>,
879+
path_cycle_detector: AppendingCycleDetector<Edge>,
880+
result: &mut R,
881+
) {
871882
let extensions = graph.outgoing_edges(self.end_node);
872883
result.reserve(extensions.size_hint().0);
873884
for extension in extensions {
874885
let mut new_path = self.clone();
886+
let mut new_cycle_detector = path_cycle_detector.clone();
875887
// If there are errors adding this edge to the path, or resolving the resulting path,
876888
// just skip the edge — it's not a fatal error.
877889
if new_path.append(graph, paths, extension).is_err() {
878890
continue;
879891
}
880-
result.push(new_path);
892+
new_cycle_detector.append(edges, extension);
893+
result.push((new_path, new_cycle_detector));
881894
}
882895
}
883896
}
@@ -903,18 +916,32 @@ impl Paths {
903916
I: IntoIterator<Item = Handle<Node>>,
904917
F: FnMut(&StackGraph, &mut Paths, Path),
905918
{
906-
let mut cycle_detector = SimilarPathDetector::new();
919+
let mut similar_path_detector_detector = SimilarPathDetector::new();
907920
let mut queue = starting_nodes
908921
.into_iter()
909-
.filter_map(|node| Path::from_node(graph, self, node))
922+
.filter_map(|node| {
923+
Path::from_node(graph, self, node).map(|p| (p, AppendingCycleDetector::new()))
924+
})
910925
.collect::<VecDeque<_>>();
911-
while let Some(path) = queue.pop_front() {
926+
let mut partials = PartialPaths::new();
927+
let mut edges = Appendages::new();
928+
while let Some((path, path_cycle_detector)) = queue.pop_front() {
912929
cancellation_flag.check("finding paths")?;
913-
if !cycle_detector.should_process_path(&path, |probe| probe.cmp(graph, self, &path)) {
930+
if !similar_path_detector_detector
931+
.should_process_path(&path, |probe| probe.cmp(graph, self, &path))
932+
{
914933
continue;
934+
} else {
935+
visit(graph, self, path.clone());
936+
}
937+
if !path_cycle_detector
938+
.is_cyclic(graph, &mut partials, &mut (), &mut edges)
939+
.into_iter()
940+
.all(|c| c == Cyclicity::StrengthensPrecondition)
941+
{
942+
} else {
943+
path.extend(graph, self, &mut edges, path_cycle_detector, &mut queue);
915944
}
916-
path.extend(graph, self, &mut queue);
917-
visit(graph, self, path);
918945
}
919946
Ok(())
920947
}

stack-graphs/src/stitching.rs

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -477,8 +477,12 @@ impl<'a> Display for DisplaySymbolStackKey<'a> {
477477
/// [`find_all_complete_paths`]: #method.find_all_complete_paths
478478
pub struct PathStitcher {
479479
candidate_paths: Vec<Handle<PartialPath>>,
480-
queue: VecDeque<Path>,
481-
next_iteration: VecDeque<Path>,
480+
queue: VecDeque<(Path, AppendingCycleDetector<OwnedOrDatabasePath>)>,
481+
next_iteration: (
482+
VecDeque<Path>,
483+
VecDeque<AppendingCycleDetector<OwnedOrDatabasePath>>,
484+
),
485+
appended_paths: Appendages<OwnedOrDatabasePath>,
482486
cycle_detector: SimilarPathDetector<Path>,
483487
max_work_per_phase: usize,
484488
#[cfg(feature = "copious-debugging")]
@@ -513,17 +517,24 @@ impl PathStitcher {
513517
copious_debugging!(" Initial node {}", node.display(graph));
514518
db.find_candidate_partial_paths_from_node(graph, partials, node, &mut candidate_paths);
515519
}
520+
let mut appended_paths = Appendages::new();
516521
let next_iteration = candidate_paths
517522
.iter()
518523
.filter_map(|partial_path| {
519-
Path::from_partial_path(graph, paths, partials, &db[*partial_path])
524+
Path::from_partial_path(graph, paths, partials, &db[*partial_path]).map(|p| {
525+
(
526+
p,
527+
AppendingCycleDetector::from(&mut appended_paths, partial_path.into()),
528+
)
529+
})
520530
})
521-
.collect();
531+
.unzip();
522532
copious_debugging!("==> End phase 0");
523533
PathStitcher {
524534
candidate_paths,
525535
queue: VecDeque::new(),
526536
next_iteration,
537+
appended_paths,
527538
cycle_detector: SimilarPathDetector::new(),
528539
// By default, there's no artificial bound on the amount of work done per phase
529540
max_work_per_phase: usize::MAX,
@@ -535,21 +546,21 @@ impl PathStitcher {
535546
/// Returns an iterator of all of the (possibly incomplete) paths that were encountered during
536547
/// the most recent phase of the path-stitching algorithm.
537548
pub fn previous_phase_paths(&self) -> impl Iterator<Item = &Path> + '_ {
538-
self.next_iteration.iter()
549+
self.next_iteration.0.iter()
539550
}
540551

541552
/// Returns a slice of all of the (possibly incomplete) paths that were encountered during the
542553
/// most recent phase of the path-stitching algorithm.
543554
pub fn previous_phase_paths_slice(&mut self) -> &[Path] {
544-
self.next_iteration.make_contiguous();
545-
self.next_iteration.as_slices().0
555+
self.next_iteration.0.make_contiguous();
556+
self.next_iteration.0.as_slices().0
546557
}
547558

548559
/// Returns a mutable slice of all of the (possibly incomplete) paths that were encountered
549560
/// during the most recent phase of the path-stitching algorithm.
550561
pub fn previous_phase_paths_slice_mut(&mut self) -> &mut [Path] {
551-
self.next_iteration.make_contiguous();
552-
self.next_iteration.as_mut_slices().0
562+
self.next_iteration.0.make_contiguous();
563+
self.next_iteration.0.as_mut_slices().0
553564
}
554565

555566
/// Sets the maximum amount of work that can be performed during each phase of the algorithm.
@@ -571,6 +582,7 @@ impl PathStitcher {
571582
partials: &mut PartialPaths,
572583
db: &mut Database,
573584
path: &Path,
585+
cycle_detector: AppendingCycleDetector<OwnedOrDatabasePath>,
574586
) -> usize {
575587
copious_debugging!("--> Candidate path {}", path.display(graph, paths));
576588
self.candidate_paths.clear();
@@ -592,29 +604,42 @@ impl PathStitcher {
592604
}
593605

594606
let extension_count = self.candidate_paths.len();
595-
self.next_iteration.reserve(extension_count);
607+
self.next_iteration.0.reserve(extension_count);
608+
self.next_iteration.1.reserve(extension_count);
596609
for extension in &self.candidate_paths {
597-
let extension = &db[*extension];
610+
let extension_path = &db[*extension];
598611
copious_debugging!(" Extend {}", path.display(graph, paths),);
599-
copious_debugging!(" with {}", extension.display(graph, partials));
612+
copious_debugging!(" with {}", extension_path.display(graph, partials));
600613
let mut new_path = path.clone();
614+
let mut new_cycle_detector = cycle_detector.clone();
601615
// If there are errors adding this partial path to the path, or resolving the resulting
602616
// path, just skip the partial path — it's not a fatal error.
603617
#[cfg_attr(not(feature = "copious-debugging"), allow(unused_variables))]
604-
if let Err(err) = new_path.append_partial_path(graph, paths, partials, extension) {
618+
if let Err(err) = new_path.append_partial_path(graph, paths, partials, extension_path) {
605619
copious_debugging!(" is invalid: {:?}", err);
606620
continue;
607621
}
622+
new_cycle_detector.append(&mut self.appended_paths, extension.into());
623+
let cycles =
624+
new_cycle_detector.is_cyclic(graph, partials, db, &mut self.appended_paths);
625+
if !cycles
626+
.into_iter()
627+
.all(|c| c == Cyclicity::StrengthensPrecondition)
628+
{
629+
copious_debugging!(" is invalid: cyclic");
630+
continue;
631+
}
608632
copious_debugging!(" is {}", new_path.display(graph, paths));
609-
self.next_iteration.push_back(new_path);
633+
self.next_iteration.0.push_back(new_path);
634+
self.next_iteration.1.push_back(new_cycle_detector);
610635
}
611636

612637
extension_count
613638
}
614639

615640
/// Returns whether the path-stitching algorithm has completed.
616641
pub fn is_complete(&self) -> bool {
617-
self.queue.is_empty() && self.next_iteration.is_empty()
642+
self.queue.is_empty() && self.next_iteration.0.is_empty()
618643
}
619644

620645
/// Runs the next phase of the path-stitching algorithm. We will have built up a set of
@@ -634,16 +659,21 @@ impl PathStitcher {
634659
db: &mut Database,
635660
) {
636661
copious_debugging!("==> Start phase {}", self.phase_number);
637-
self.queue.extend(self.next_iteration.drain(..));
662+
self.queue.extend(
663+
self.next_iteration
664+
.0
665+
.drain(..)
666+
.zip(self.next_iteration.1.drain(..)),
667+
);
638668
let mut work_performed = 0;
639-
while let Some(path) = self.queue.pop_front() {
669+
while let Some((path, cycle_detector)) = self.queue.pop_front() {
640670
if !self
641671
.cycle_detector
642672
.should_process_path(&path, |probe| probe.cmp(graph, paths, &path))
643673
{
644674
continue;
645675
}
646-
work_performed += self.stitch_path(graph, paths, partials, db, &path);
676+
work_performed += self.stitch_path(graph, paths, partials, db, &path, cycle_detector);
647677
if work_performed >= self.max_work_per_phase {
648678
break;
649679
}
@@ -795,8 +825,8 @@ impl ForwardPartialPathStitcher {
795825
candidate_partial_paths,
796826
queue: VecDeque::new(),
797827
next_iteration,
798-
similar_path_detector: SimilarPathDetector::new(),
799828
appended_paths,
829+
similar_path_detector: SimilarPathDetector::new(),
800830
// By default, there's no artificial bound on the amount of work done per phase
801831
max_work_per_phase: usize::MAX,
802832
#[cfg(feature = "copious-debugging")]

0 commit comments

Comments
 (0)