1use alloc::{
39 collections::{BTreeMap, BTreeSet, VecDeque},
40 vec::Vec,
41};
42use core::cmp::Ordering;
43
44use super::vertex_match_inference::infer_vertex_matches;
45
46pub type MatchedEdgePair<N> = ((N, N), (N, N));
48
49pub trait CliqueInfo {
54 type NodeId: Eq + Copy + Ord + core::fmt::Debug;
56
57 fn clique(&self) -> &[usize];
59
60 fn matched_edges(&self) -> &[MatchedEdgePair<Self::NodeId>];
63
64 fn vertex_matches(&self) -> &[(Self::NodeId, Self::NodeId)];
66
67 fn fragment_count(&self) -> usize;
70
71 fn largest_fragment_edge_count(&self) -> usize;
73
74 fn largest_fragment_atom_count(&self) -> usize;
76
77 #[inline]
79 fn largest_fragment_size(&self) -> usize {
80 self.largest_fragment_edge_count()
81 }
82}
83
84#[derive(Clone, Debug, PartialEq, Eq, Hash)]
89pub struct EagerCliqueInfo<N> {
90 clique: Vec<usize>,
91 matched_edges: Vec<MatchedEdgePair<N>>,
92 vertex_matches: Vec<(N, N)>,
93 fragment_count: usize,
94 largest_fragment_edge_count: usize,
95 largest_fragment_atom_count: usize,
96}
97
98impl<N> EagerCliqueInfo<N>
99where
100 N: Eq + Copy + Ord + core::fmt::Debug,
101{
102 #[must_use]
118 pub fn new<F>(
119 clique: Vec<usize>,
120 vertex_pairs: &[(usize, usize)],
121 edge_map_first: &[(N, N)],
122 edge_map_second: &[(N, N)],
123 disambiguate: F,
124 ) -> Self
125 where
126 F: FnMut(N, N, N, N) -> bool,
127 {
128 let matched_edges: Vec<MatchedEdgePair<N>> = clique
130 .iter()
131 .map(|&k| {
132 let (lg1, lg2) = vertex_pairs[k];
133 (edge_map_first[lg1], edge_map_second[lg2])
134 })
135 .collect();
136
137 let vertex_matches = infer_vertex_matches(
139 &clique,
140 vertex_pairs,
141 edge_map_first,
142 edge_map_second,
143 disambiguate,
144 );
145
146 let g1_edges: Vec<(N, N)> = matched_edges.iter().map(|&(e1, _)| e1).collect();
148 let (fragment_count, largest_fragment_edge_count, largest_fragment_atom_count) =
149 compute_fragments(&g1_edges);
150
151 Self {
152 clique,
153 matched_edges,
154 vertex_matches,
155 fragment_count,
156 largest_fragment_edge_count,
157 largest_fragment_atom_count,
158 }
159 }
160}
161
162impl<N> CliqueInfo for EagerCliqueInfo<N>
163where
164 N: Eq + Copy + Ord + core::fmt::Debug,
165{
166 type NodeId = N;
167
168 #[inline]
169 fn clique(&self) -> &[usize] {
170 &self.clique
171 }
172
173 #[inline]
174 fn matched_edges(&self) -> &[MatchedEdgePair<N>] {
175 &self.matched_edges
176 }
177
178 #[inline]
179 fn vertex_matches(&self) -> &[(N, N)] {
180 &self.vertex_matches
181 }
182
183 #[inline]
184 fn fragment_count(&self) -> usize {
185 self.fragment_count
186 }
187
188 #[inline]
189 fn largest_fragment_edge_count(&self) -> usize {
190 self.largest_fragment_edge_count
191 }
192
193 #[inline]
194 fn largest_fragment_atom_count(&self) -> usize {
195 self.largest_fragment_atom_count
196 }
197}
198
199fn compute_fragments<N: Eq + Copy + Ord>(edges: &[(N, N)]) -> (usize, usize, usize) {
207 if edges.is_empty() {
208 return (0, 0, 0);
209 }
210
211 let mut adj: BTreeMap<N, Vec<N>> = BTreeMap::new();
213 for &(u, v) in edges {
214 adj.entry(u).or_default().push(v);
215 adj.entry(v).or_default().push(u);
216 }
217
218 let mut visited: BTreeSet<N> = BTreeSet::new();
220 let mut queue: VecDeque<N> = VecDeque::new();
221 let mut num_components = 0usize;
222 let mut largest_component_edges = 0usize;
223 let mut largest_component_vertices = 0usize;
224
225 for &start in adj.keys() {
226 if visited.contains(&start) {
227 continue;
228 }
229
230 visited.insert(start);
232 queue.push_back(start);
233 let mut component_vertices: Vec<N> = Vec::new();
234
235 while let Some(v) = queue.pop_front() {
236 component_vertices.push(v);
237 if let Some(neighbors) = adj.get(&v) {
238 for &u in neighbors {
239 if visited.insert(u) {
240 queue.push_back(u);
241 }
242 }
243 }
244 }
245
246 let component_set: BTreeSet<N> = component_vertices.iter().copied().collect();
250 let component_edges = edges
251 .iter()
252 .filter(|&&(u, v)| component_set.contains(&u) && component_set.contains(&v))
253 .count();
254
255 num_components += 1;
256 if component_edges > largest_component_edges {
257 largest_component_edges = component_edges;
258 }
259 if component_vertices.len() > largest_component_vertices {
260 largest_component_vertices = component_vertices.len();
261 }
262 }
263
264 (num_components, largest_component_edges, largest_component_vertices)
265}
266
267pub trait CliqueRanker<I: CliqueInfo> {
273 fn compare(&self, a: &I, b: &I) -> Ordering;
276}
277
278pub trait CliqueRankerExt: Sized {
283 #[inline]
286 fn then<R>(self, other: R) -> ChainedRanker<Self, R> {
287 ChainedRanker { first: self, second: other }
288 }
289}
290
291impl<T> CliqueRankerExt for T {}
292
293#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
298pub struct ChainedRanker<R1, R2> {
299 first: R1,
300 second: R2,
301}
302
303impl<I: CliqueInfo, R1: CliqueRanker<I>, R2: CliqueRanker<I>> CliqueRanker<I>
304 for ChainedRanker<R1, R2>
305{
306 #[inline]
307 fn compare(&self, a: &I, b: &I) -> Ordering {
308 self.first.compare(a, b).then_with(|| self.second.compare(a, b))
309 }
310}
311
312#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
318pub struct FragmentCountRanker;
319
320impl<I: CliqueInfo> CliqueRanker<I> for FragmentCountRanker {
321 #[inline]
322 fn compare(&self, a: &I, b: &I) -> Ordering {
323 a.fragment_count().cmp(&b.fragment_count())
324 }
325}
326
327#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
333pub struct LargestFragmentRanker;
334
335impl<I: CliqueInfo> CliqueRanker<I> for LargestFragmentRanker {
336 #[inline]
337 fn compare(&self, a: &I, b: &I) -> Ordering {
338 b.largest_fragment_edge_count().cmp(&a.largest_fragment_edge_count())
339 }
340}
341
342#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
344pub enum LargestFragmentMetric {
345 Edges,
347 Atoms,
349}
350
351#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
353pub struct LargestFragmentMetricRanker {
354 metric: LargestFragmentMetric,
355}
356
357impl LargestFragmentMetricRanker {
358 #[inline]
360 #[must_use]
361 pub const fn new(metric: LargestFragmentMetric) -> Self {
362 Self { metric }
363 }
364}
365
366impl Default for LargestFragmentMetricRanker {
367 #[inline]
368 fn default() -> Self {
369 Self::new(LargestFragmentMetric::Edges)
370 }
371}
372
373impl<I: CliqueInfo> CliqueRanker<I> for LargestFragmentMetricRanker {
374 #[inline]
375 fn compare(&self, a: &I, b: &I) -> Ordering {
376 match self.metric {
377 LargestFragmentMetric::Edges => {
378 b.largest_fragment_edge_count().cmp(&a.largest_fragment_edge_count())
379 }
380 LargestFragmentMetric::Atoms => {
381 b.largest_fragment_atom_count().cmp(&a.largest_fragment_atom_count())
382 }
383 }
384 }
385}
386
387pub struct FnRanker<F> {
406 compare_fn: F,
407}
408
409impl<F> FnRanker<F> {
410 #[inline]
412 #[must_use]
413 pub fn new(compare_fn: F) -> Self {
414 Self { compare_fn }
415 }
416}
417
418impl<I: CliqueInfo, F: Fn(&I, &I) -> Ordering> CliqueRanker<I> for FnRanker<F> {
419 #[inline]
420 fn compare(&self, a: &I, b: &I) -> Ordering {
421 (self.compare_fn)(a, b)
422 }
423}
424
425#[cfg(test)]
426mod tests {
427 use super::*;
428
429 #[test]
430 fn test_compute_fragments_empty() {
431 let (count, largest_edges, largest_atoms) = compute_fragments::<u32>(&[]);
432 assert_eq!(count, 0);
433 assert_eq!(largest_edges, 0);
434 assert_eq!(largest_atoms, 0);
435 }
436
437 #[test]
438 fn test_compute_fragments_single_edge() {
439 let (count, largest_edges, largest_atoms) = compute_fragments(&[(0u32, 1)]);
440 assert_eq!(count, 1);
441 assert_eq!(largest_edges, 1);
442 assert_eq!(largest_atoms, 2);
443 }
444
445 #[test]
446 fn test_compute_fragments_path() {
447 let (count, largest_edges, largest_atoms) = compute_fragments(&[(0u32, 1), (1, 2), (2, 3)]);
449 assert_eq!(count, 1);
450 assert_eq!(largest_edges, 3);
451 assert_eq!(largest_atoms, 4);
452 }
453
454 #[test]
455 fn test_compute_fragments_two_disjoint() {
456 let (count, largest_edges, largest_atoms) = compute_fragments(&[(0u32, 1), (5, 6)]);
458 assert_eq!(count, 2);
459 assert_eq!(largest_edges, 1);
460 assert_eq!(largest_atoms, 2);
461 }
462
463 #[test]
464 fn test_compute_fragments_uneven() {
465 let (count, largest_edges, largest_atoms) = compute_fragments(&[(0u32, 1), (1, 2), (5, 6)]);
468 assert_eq!(count, 2);
469 assert_eq!(largest_edges, 2);
470 assert_eq!(largest_atoms, 3);
471 }
472
473 #[test]
474 fn test_compute_fragments_triangle() {
475 let (count, largest_edges, largest_atoms) = compute_fragments(&[(0u32, 1), (1, 2), (0, 2)]);
476 assert_eq!(count, 1);
477 assert_eq!(largest_edges, 3);
478 assert_eq!(largest_atoms, 3);
479 }
480}