Skip to content

Commit 0e355aa

Browse files
committed
use Vec in find_shortest_cycle to get deterministic outputs
1 parent b1804b0 commit 0e355aa

File tree

2 files changed

+55
-43
lines changed

2 files changed

+55
-43
lines changed

rust/src/graph/cycles.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,19 @@ impl Graph {
1010
module: ModuleToken,
1111
as_package: bool,
1212
) -> GrimpResult<Option<Vec<ModuleToken>>> {
13-
if as_package {
14-
pathfinding::find_shortest_cycle(
15-
self,
16-
&module.conv::<FxHashSet<_>>().with_descendants(self),
17-
&FxHashSet::default(),
18-
&FxHashMap::default(),
19-
)
13+
let modules: Vec<ModuleToken> = if as_package {
14+
let mut vec = module.conv::<Vec<_>>().with_descendants(self);
15+
vec.sort_by_key(|token| self.get_module(*token).unwrap().name());
16+
vec
2017
} else {
21-
pathfinding::find_shortest_cycle(
22-
self,
23-
&module.conv::<FxHashSet<_>>(),
24-
&FxHashSet::default(),
25-
&FxHashMap::default(),
26-
)
27-
}
18+
let vec: Vec<_> = module.conv::<Vec<ModuleToken>>();
19+
vec
20+
};
21+
pathfinding::find_shortest_cycle(
22+
self,
23+
&modules,
24+
&FxHashSet::default(),
25+
&FxHashMap::default(),
26+
)
2827
}
2928
}

rust/src/graph/pathfinding.rs

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,18 @@ pub fn find_shortest_path(
4141
return Err(GrimpError::SharedDescendants);
4242
}
4343

44+
let predecessors: FxIndexMap<ModuleToken, Option<ModuleToken>> = from_modules
45+
.clone()
46+
.into_iter()
47+
.map(|m| (m, None))
48+
.collect();
49+
let successors: FxIndexMap<ModuleToken, Option<ModuleToken>> =
50+
to_modules.clone().into_iter().map(|m| (m, None)).collect();
51+
4452
_find_shortest_path(
4553
graph,
46-
from_modules,
47-
to_modules,
54+
predecessors,
55+
successors,
4856
excluded_modules,
4957
excluded_imports,
5058
)
@@ -53,7 +61,7 @@ pub fn find_shortest_path(
5361
/// Finds the shortest cycle from `modules` to `modules`, via a bidirectional BFS.
5462
pub fn find_shortest_cycle(
5563
graph: &Graph,
56-
modules: &FxHashSet<ModuleToken>,
64+
modules: &[ModuleToken],
5765
excluded_modules: &FxHashSet<ModuleToken>,
5866
excluded_imports: &FxHashMap<ModuleToken, FxHashSet<ModuleToken>>,
5967
) -> GrimpResult<Option<Vec<ModuleToken>>> {
@@ -64,56 +72,61 @@ pub fn find_shortest_cycle(
6472
excluded_imports.entry(*m2).or_default().insert(*m1);
6573
}
6674

67-
_find_shortest_path(graph, modules, modules, excluded_modules, &excluded_imports)
75+
let predecessors: FxIndexMap<ModuleToken, Option<ModuleToken>> = modules
76+
.iter()
77+
.cloned()
78+
.map(|m| (m, None))
79+
.collect();
80+
81+
let successors: FxIndexMap<ModuleToken, Option<ModuleToken>> = predecessors
82+
.clone();
83+
84+
_find_shortest_path(graph, predecessors, successors, excluded_modules, &excluded_imports)
6885
}
6986

7087
fn _find_shortest_path(
7188
graph: &Graph,
72-
from_modules: &FxHashSet<ModuleToken>,
73-
to_modules: &FxHashSet<ModuleToken>,
89+
mut predecessors: FxIndexMap<ModuleToken, Option<ModuleToken>>,
90+
mut successors: FxIndexMap<ModuleToken, Option<ModuleToken>>,
7491
excluded_modules: &FxHashSet<ModuleToken>,
7592
excluded_imports: &FxHashMap<ModuleToken, FxHashSet<ModuleToken>>,
7693
) -> GrimpResult<Option<Vec<ModuleToken>>> {
77-
let mut predecessors: FxIndexMap<ModuleToken, Option<ModuleToken>> = from_modules
78-
.clone()
79-
.into_iter()
80-
.map(|m| (m, None))
81-
.collect();
82-
let mut successors: FxIndexMap<ModuleToken, Option<ModuleToken>> =
83-
to_modules.clone().into_iter().map(|m| (m, None)).collect();
94+
8495

8596
let mut i_forwards = 0;
8697
let mut i_backwards = 0;
8798
let middle = 'l: loop {
8899
for _ in 0..(predecessors.len() - i_forwards) {
89100
let module = *predecessors.get_index(i_forwards).unwrap().0;
90-
let next_modules = graph.imports.get(module).unwrap();
101+
let mut next_modules: Vec<_> = graph.imports.get(module).unwrap().iter().cloned().collect();
102+
next_modules.sort_by_key(|next_module| graph.get_module(*next_module).unwrap().name());
91103
for next_module in next_modules {
92-
if import_is_excluded(&module, next_module, excluded_modules, excluded_imports) {
104+
if import_is_excluded(&module, &next_module, excluded_modules, excluded_imports) {
93105
continue;
94106
}
95-
if !predecessors.contains_key(next_module) {
96-
predecessors.insert(*next_module, Some(module));
107+
if !predecessors.contains_key(&next_module) {
108+
predecessors.insert(next_module, Some(module));
97109
}
98-
if successors.contains_key(next_module) {
99-
break 'l Some(*next_module);
110+
if successors.contains_key(&next_module) {
111+
break 'l Some(next_module);
100112
}
101113
}
102114
i_forwards += 1;
103115
}
104116

105117
for _ in 0..(successors.len() - i_backwards) {
106118
let module = *successors.get_index(i_backwards).unwrap().0;
107-
let next_modules = graph.reverse_imports.get(module).unwrap();
119+
let mut next_modules: Vec<_> = graph.reverse_imports.get(module).unwrap().iter().cloned().collect();
120+
next_modules.sort_by_key(|next_module| graph.get_module(*next_module).unwrap().name());
108121
for next_module in next_modules {
109-
if import_is_excluded(next_module, &module, excluded_modules, excluded_imports) {
122+
if import_is_excluded(&next_module, &module, excluded_modules, excluded_imports) {
110123
continue;
111124
}
112-
if !successors.contains_key(next_module) {
113-
successors.insert(*next_module, Some(module));
125+
if !successors.contains_key(&next_module) {
126+
successors.insert(next_module, Some(module));
114127
}
115-
if predecessors.contains_key(next_module) {
116-
break 'l Some(*next_module);
128+
if predecessors.contains_key(&next_module) {
129+
break 'l Some(next_module);
117130
}
118131
}
119132
i_backwards += 1;
@@ -184,7 +197,7 @@ mod test_find_shortest_cycle {
184197

185198
let path = find_shortest_cycle(
186199
&graph,
187-
&foo.into(),
200+
&[foo],
188201
&FxHashSet::default(),
189202
&FxHashMap::default(),
190203
)?;
@@ -194,7 +207,7 @@ mod test_find_shortest_cycle {
194207

195208
let path = find_shortest_cycle(
196209
&graph,
197-
&foo.into(),
210+
&[foo],
198211
&FxHashSet::default(),
199212
&FxHashMap::default(),
200213
)?;
@@ -214,7 +227,7 @@ mod test_find_shortest_cycle {
214227

215228
let path = find_shortest_cycle(
216229
&graph,
217-
&foo.into(),
230+
&[foo],
218231
&FxHashSet::default(),
219232
&FxHashMap::default(),
220233
)?;
@@ -250,7 +263,7 @@ mod test_find_shortest_cycle {
250263

251264
let path = find_shortest_cycle(
252265
&graph,
253-
&FxHashSet::from_iter([red, blue]),
266+
&Vec::from_iter([red, blue]),
254267
&FxHashSet::default(),
255268
&FxHashMap::default(),
256269
)?;

0 commit comments

Comments
 (0)