use alloc::{
collections::{BTreeMap, BTreeSet, VecDeque},
vec::Vec,
};
use core::cmp::Ordering;
use super::vertex_match_inference::infer_vertex_matches;
pub type MatchedEdgePair<N> = ((N, N), (N, N));
pub trait CliqueInfo {
type NodeId: Eq + Copy + Ord + core::fmt::Debug;
fn clique(&self) -> &[usize];
fn matched_edges(&self) -> &[MatchedEdgePair<Self::NodeId>];
fn vertex_matches(&self) -> &[(Self::NodeId, Self::NodeId)];
fn fragment_count(&self) -> usize;
fn largest_fragment_edge_count(&self) -> usize;
fn largest_fragment_atom_count(&self) -> usize;
#[inline]
fn largest_fragment_size(&self) -> usize {
self.largest_fragment_edge_count()
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct EagerCliqueInfo<N> {
clique: Vec<usize>,
matched_edges: Vec<MatchedEdgePair<N>>,
vertex_matches: Vec<(N, N)>,
fragment_count: usize,
largest_fragment_edge_count: usize,
largest_fragment_atom_count: usize,
}
impl<N> EagerCliqueInfo<N>
where
N: Eq + Copy + Ord + core::fmt::Debug,
{
#[must_use]
pub fn new<F>(
clique: Vec<usize>,
vertex_pairs: &[(usize, usize)],
edge_map_first: &[(N, N)],
edge_map_second: &[(N, N)],
disambiguate: F,
) -> Self
where
F: FnMut(N, N, N, N) -> bool,
{
let matched_edges: Vec<MatchedEdgePair<N>> = clique
.iter()
.map(|&k| {
let (lg1, lg2) = vertex_pairs[k];
(edge_map_first[lg1], edge_map_second[lg2])
})
.collect();
let vertex_matches = infer_vertex_matches(
&clique,
vertex_pairs,
edge_map_first,
edge_map_second,
disambiguate,
);
let g1_edges: Vec<(N, N)> = matched_edges.iter().map(|&(e1, _)| e1).collect();
let (fragment_count, largest_fragment_edge_count, largest_fragment_atom_count) =
compute_fragments(&g1_edges);
Self {
clique,
matched_edges,
vertex_matches,
fragment_count,
largest_fragment_edge_count,
largest_fragment_atom_count,
}
}
}
impl<N> CliqueInfo for EagerCliqueInfo<N>
where
N: Eq + Copy + Ord + core::fmt::Debug,
{
type NodeId = N;
#[inline]
fn clique(&self) -> &[usize] {
&self.clique
}
#[inline]
fn matched_edges(&self) -> &[MatchedEdgePair<N>] {
&self.matched_edges
}
#[inline]
fn vertex_matches(&self) -> &[(N, N)] {
&self.vertex_matches
}
#[inline]
fn fragment_count(&self) -> usize {
self.fragment_count
}
#[inline]
fn largest_fragment_edge_count(&self) -> usize {
self.largest_fragment_edge_count
}
#[inline]
fn largest_fragment_atom_count(&self) -> usize {
self.largest_fragment_atom_count
}
}
fn compute_fragments<N: Eq + Copy + Ord>(edges: &[(N, N)]) -> (usize, usize, usize) {
if edges.is_empty() {
return (0, 0, 0);
}
let mut adj: BTreeMap<N, Vec<N>> = BTreeMap::new();
for &(u, v) in edges {
adj.entry(u).or_default().push(v);
adj.entry(v).or_default().push(u);
}
let mut visited: BTreeSet<N> = BTreeSet::new();
let mut queue: VecDeque<N> = VecDeque::new();
let mut num_components = 0usize;
let mut largest_component_edges = 0usize;
let mut largest_component_vertices = 0usize;
for &start in adj.keys() {
if visited.contains(&start) {
continue;
}
visited.insert(start);
queue.push_back(start);
let mut component_vertices: Vec<N> = Vec::new();
while let Some(v) = queue.pop_front() {
component_vertices.push(v);
if let Some(neighbors) = adj.get(&v) {
for &u in neighbors {
if visited.insert(u) {
queue.push_back(u);
}
}
}
}
let component_set: BTreeSet<N> = component_vertices.iter().copied().collect();
let component_edges = edges
.iter()
.filter(|&&(u, v)| component_set.contains(&u) && component_set.contains(&v))
.count();
num_components += 1;
if component_edges > largest_component_edges {
largest_component_edges = component_edges;
}
if component_vertices.len() > largest_component_vertices {
largest_component_vertices = component_vertices.len();
}
}
(num_components, largest_component_edges, largest_component_vertices)
}
pub trait CliqueRanker<I: CliqueInfo> {
fn compare(&self, a: &I, b: &I) -> Ordering;
}
pub trait CliqueRankerExt: Sized {
#[inline]
fn then<R>(self, other: R) -> ChainedRanker<Self, R> {
ChainedRanker { first: self, second: other }
}
}
impl<T> CliqueRankerExt for T {}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ChainedRanker<R1, R2> {
first: R1,
second: R2,
}
impl<I: CliqueInfo, R1: CliqueRanker<I>, R2: CliqueRanker<I>> CliqueRanker<I>
for ChainedRanker<R1, R2>
{
#[inline]
fn compare(&self, a: &I, b: &I) -> Ordering {
self.first.compare(a, b).then_with(|| self.second.compare(a, b))
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct FragmentCountRanker;
impl<I: CliqueInfo> CliqueRanker<I> for FragmentCountRanker {
#[inline]
fn compare(&self, a: &I, b: &I) -> Ordering {
a.fragment_count().cmp(&b.fragment_count())
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct LargestFragmentRanker;
impl<I: CliqueInfo> CliqueRanker<I> for LargestFragmentRanker {
#[inline]
fn compare(&self, a: &I, b: &I) -> Ordering {
b.largest_fragment_edge_count().cmp(&a.largest_fragment_edge_count())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum LargestFragmentMetric {
Edges,
Atoms,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct LargestFragmentMetricRanker {
metric: LargestFragmentMetric,
}
impl LargestFragmentMetricRanker {
#[inline]
#[must_use]
pub const fn new(metric: LargestFragmentMetric) -> Self {
Self { metric }
}
}
impl Default for LargestFragmentMetricRanker {
#[inline]
fn default() -> Self {
Self::new(LargestFragmentMetric::Edges)
}
}
impl<I: CliqueInfo> CliqueRanker<I> for LargestFragmentMetricRanker {
#[inline]
fn compare(&self, a: &I, b: &I) -> Ordering {
match self.metric {
LargestFragmentMetric::Edges => {
b.largest_fragment_edge_count().cmp(&a.largest_fragment_edge_count())
}
LargestFragmentMetric::Atoms => {
b.largest_fragment_atom_count().cmp(&a.largest_fragment_atom_count())
}
}
}
}
pub struct FnRanker<F> {
compare_fn: F,
}
impl<F> FnRanker<F> {
#[inline]
#[must_use]
pub fn new(compare_fn: F) -> Self {
Self { compare_fn }
}
}
impl<I: CliqueInfo, F: Fn(&I, &I) -> Ordering> CliqueRanker<I> for FnRanker<F> {
#[inline]
fn compare(&self, a: &I, b: &I) -> Ordering {
(self.compare_fn)(a, b)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compute_fragments_empty() {
let (count, largest_edges, largest_atoms) = compute_fragments::<u32>(&[]);
assert_eq!(count, 0);
assert_eq!(largest_edges, 0);
assert_eq!(largest_atoms, 0);
}
#[test]
fn test_compute_fragments_single_edge() {
let (count, largest_edges, largest_atoms) = compute_fragments(&[(0u32, 1)]);
assert_eq!(count, 1);
assert_eq!(largest_edges, 1);
assert_eq!(largest_atoms, 2);
}
#[test]
fn test_compute_fragments_path() {
let (count, largest_edges, largest_atoms) = compute_fragments(&[(0u32, 1), (1, 2), (2, 3)]);
assert_eq!(count, 1);
assert_eq!(largest_edges, 3);
assert_eq!(largest_atoms, 4);
}
#[test]
fn test_compute_fragments_two_disjoint() {
let (count, largest_edges, largest_atoms) = compute_fragments(&[(0u32, 1), (5, 6)]);
assert_eq!(count, 2);
assert_eq!(largest_edges, 1);
assert_eq!(largest_atoms, 2);
}
#[test]
fn test_compute_fragments_uneven() {
let (count, largest_edges, largest_atoms) = compute_fragments(&[(0u32, 1), (1, 2), (5, 6)]);
assert_eq!(count, 2);
assert_eq!(largest_edges, 2);
assert_eq!(largest_atoms, 3);
}
#[test]
fn test_compute_fragments_triangle() {
let (count, largest_edges, largest_atoms) = compute_fragments(&[(0u32, 1), (1, 2), (0, 2)]);
assert_eq!(count, 1);
assert_eq!(largest_edges, 3);
assert_eq!(largest_atoms, 3);
}
}