Skip to content

Commit 4713d73

Browse files
authored
Merge pull request #238 from Peter554/improve-find-shortest-chains
Optimize find_shortest_chains
2 parents 3176007 + d57385e commit 4713d73

3 files changed

Lines changed: 55 additions & 41 deletions

File tree

CHANGELOG.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Unreleased
77

88
* Add closed layers to layer contract.
99
* Rename default repository branch to 'main'.
10+
* Optimise `find_shortest_chains` query.
1011

1112
3.9 (2025-05-05)
1213
----------------

rust/src/graph/higher_order_queries.rs

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@ use crate::errors::GrimpResult;
22
use crate::graph::{ExtendWithDescendants, Graph, ModuleToken};
33
use derive_new::new;
44
use getset::Getters;
5-
use itertools::Itertools;
65
use rayon::prelude::*;
7-
use rustc_hash::{FxHashMap, FxHashSet};
6+
use rustc_hash::FxHashSet;
87

98
use tap::prelude::*;
109

@@ -146,35 +145,18 @@ impl Graph {
146145
let excluded_modules =
147146
all_layers_modules - &(&from_layer_with_descendants | &to_layer_with_descendants);
148147

149-
// Disallow chains via these imports.
150-
// We'll add chains to this set as we discover them.
151-
let mut excluded_imports = FxHashMap::default();
148+
let chains = self._find_shortest_chains(
149+
&from_layer_with_descendants,
150+
&to_layer_with_descendants,
151+
&excluded_modules,
152+
)?;
152153

153154
// Collect direct imports...
154155
let mut direct_imports = vec![];
155156
// ...and the middles of any indirect imports.
156157
let mut middles = vec![];
157-
loop {
158-
let chain = self.find_shortest_chain_with_excluded_modules_and_imports(
159-
&from_layer_with_descendants,
160-
&to_layer_with_descendants,
161-
&excluded_modules,
162-
&excluded_imports,
163-
)?;
164-
165-
if chain.is_none() {
166-
break;
167-
}
168-
let chain = chain.unwrap();
169-
170-
// Exclude this chain from further searching.
171-
for (importer, imported) in chain.iter().tuple_windows() {
172-
excluded_imports
173-
.entry(*importer)
174-
.or_default()
175-
.insert(*imported);
176-
}
177158

159+
for chain in chains {
178160
let (head, middle, tail) = self.split_chain(&chain);
179161
match middle {
180162
Some(middle) => middles.push(middle),

rust/src/graph/import_chain_queries.rs

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -100,24 +100,55 @@ impl Graph {
100100
downstream_modules.extend_with_descendants(self);
101101
upstream_modules.extend_with_descendants(self);
102102
}
103-
let all_modules = &downstream_modules | &upstream_modules;
104-
105-
let chains = downstream_modules
106-
.iter()
107-
.cartesian_product(upstream_modules.iter())
108-
.filter_map(|(downstream_module, upstream_module)| {
109-
let excluded_modules =
110-
&all_modules - &FxHashSet::from_iter([*downstream_module, *upstream_module]);
111-
self.find_shortest_chain_with_excluded_modules_and_imports(
112-
&(*downstream_module).into(),
113-
&(*upstream_module).into(),
114-
&excluded_modules,
115-
&FxHashMap::default(),
116-
)
117-
.unwrap()
118-
})
103+
104+
let chains = self
105+
._find_shortest_chains(
106+
&downstream_modules,
107+
&upstream_modules,
108+
&FxHashSet::from_iter([]),
109+
)?
110+
.into_iter()
119111
.collect();
120112

121113
Ok(chains)
122114
}
115+
116+
pub(crate) fn _find_shortest_chains(
117+
&self,
118+
from_modules: &FxHashSet<ModuleToken>,
119+
to_modules: &FxHashSet<ModuleToken>,
120+
excluded_modules: &FxHashSet<ModuleToken>,
121+
) -> GrimpResult<Vec<Vec<ModuleToken>>> {
122+
let mut chains = vec![];
123+
124+
// Disallow chains via these imports.
125+
// We'll add chains to this set as we discover them.
126+
let mut excluded_imports = FxHashMap::default();
127+
128+
loop {
129+
let chain = self.find_shortest_chain_with_excluded_modules_and_imports(
130+
from_modules,
131+
to_modules,
132+
excluded_modules,
133+
&excluded_imports,
134+
)?;
135+
136+
if chain.is_none() {
137+
break;
138+
}
139+
let chain = chain.unwrap();
140+
141+
// Exclude this chain from further searching.
142+
for (importer, imported) in chain.iter().tuple_windows() {
143+
excluded_imports
144+
.entry(*importer)
145+
.or_default()
146+
.insert(*imported);
147+
}
148+
149+
chains.push(chain);
150+
}
151+
152+
Ok(chains)
153+
}
123154
}

0 commit comments

Comments
 (0)