use std::collections::HashMap;
use super::canonical::{canonical_by, DirectedClassId};
use super::snapshot::{DirectedGraphAdapter, DirectedSnapshot};
use crate::canonical::perms;
pub const MAX_K: usize = 5;
#[derive(Clone, Debug)]
pub struct DirectedInstance<N> {
pub nodes: Vec<N>,
pub class: DirectedClassId,
}
#[derive(Clone, Copy, Debug)]
pub struct DirectedSelector {
k: usize,
}
impl DirectedSelector {
#[must_use]
pub fn weakly_connected_k_subsets(k: usize) -> Self {
assert!(
(2..=MAX_K).contains(&k),
"directed graphlet order k must be in 2..={MAX_K}, got {k}"
);
DirectedSelector { k }
}
#[inline]
#[must_use]
pub fn k(&self) -> usize {
self.k
}
}
pub type DirectedCensus = HashMap<DirectedClassId, u64>;
pub(crate) fn for_each_subset<N: Copy>(
snapshot: &DirectedSnapshot<N>,
k: usize,
mut f: impl FnMut(&[usize]),
) {
fn ext_of<N: Copy>(
s: &DirectedSnapshot<N>,
sub: &[usize],
w: usize,
v: usize,
ext: &[usize],
) -> Vec<usize> {
let mut e = ext.to_vec();
for &u in s.undirected_neighbors(w) {
if u <= v || sub.contains(&u) || e.contains(&u) {
continue;
}
if !sub.iter().any(|&x| s.undirected_adjacent(x, u)) {
e.push(u);
}
}
e
}
fn rec<N: Copy>(
s: &DirectedSnapshot<N>,
sub: &mut Vec<usize>,
ext: &mut Vec<usize>,
v: usize,
k: usize,
f: &mut impl FnMut(&[usize]),
) {
if sub.len() == k {
f(sub);
return;
}
while let Some(w) = ext.pop() {
let mut child = ext_of(s, sub, w, v, ext);
sub.push(w);
rec(s, sub, &mut child, v, k, f);
sub.pop();
}
}
for v in 0..snapshot.len() {
let mut sub = vec![v];
let mut ext: Vec<usize> = snapshot
.undirected_neighbors(v)
.iter()
.copied()
.filter(|&u| u > v)
.collect();
rec(snapshot, &mut sub, &mut ext, v, k, &mut f);
}
}
pub struct DirectedInstances<N> {
items: std::vec::IntoIter<DirectedInstance<N>>,
}
impl<N> Iterator for DirectedInstances<N> {
type Item = DirectedInstance<N>;
fn next(&mut self) -> Option<DirectedInstance<N>> {
self.items.next()
}
}
#[must_use]
pub fn enumerate_directed<G>(g: G, sel: &DirectedSelector) -> DirectedInstances<G::NodeId>
where
G: DirectedGraphAdapter,
{
let snapshot = DirectedSnapshot::new(g);
let ps = perms(sel.k);
let mut items = Vec::new();
for_each_subset(&snapshot, sel.k, |sub| {
let class = DirectedClassId(canonical_by(sel.k, &ps, |i, j| {
snapshot.has_arc(sub[i], sub[j])
}));
let nodes = sub.iter().map(|&i| snapshot.id(i)).collect();
items.push(DirectedInstance { nodes, class });
});
DirectedInstances {
items: items.into_iter(),
}
}
#[must_use]
pub fn count_directed<G>(g: G, sel: &DirectedSelector) -> DirectedCensus
where
G: DirectedGraphAdapter,
{
let snapshot = DirectedSnapshot::new(g);
let ps = perms(sel.k);
let mut census: DirectedCensus = HashMap::new();
for_each_subset(&snapshot, sel.k, |sub| {
let class = DirectedClassId(canonical_by(sel.k, &ps, |i, j| {
snapshot.has_arc(sub[i], sub[j])
}));
*census.entry(class).or_insert(0) += 1;
});
census
}