use crate::prelude::{EdgeViewOps, GraphViewOps, NodeViewOps};
use raphtory_api::core::entities::VID;
use std::{
cmp::{max, min},
collections::HashSet,
};
struct SlidingWindows<I> {
iter: I,
window_size: usize,
}
impl<I> SlidingWindows<I> {
fn new(iter: I, window_size: usize) -> Self {
SlidingWindows { iter, window_size }
}
}
impl<I> Iterator for SlidingWindows<I>
where
I: Iterator,
I::Item: Clone,
{
type Item = Vec<I::Item>;
fn next(&mut self) -> Option<Self::Item> {
let mut window = Vec::with_capacity(self.window_size);
for _ in 0..self.window_size {
if let Some(item) = self.iter.next() {
window.push(item);
} else {
return None;
}
}
Some(window)
}
}
pub fn temporal_rich_club_coefficient<'a, I, G1, G2>(
graph: &G2,
views: I,
k: usize,
window_size: usize,
) -> f64
where
I: IntoIterator<Item = G1>,
G1: GraphViewOps<'a>,
G2: GraphViewOps<'a>,
{
let s_k: HashSet<VID> = graph
.nodes()
.into_iter()
.filter(|v| v.degree() >= k)
.map(|v| v.node)
.collect();
if s_k.len() <= 1 {
return 0.0f64;
}
let temp_rich_club_val = SlidingWindows::new(views.into_iter(), window_size)
.map(|window| intermediate_rich_club_coef(s_k.clone(), window))
.reduce(f64::max)
.unwrap_or(0.0);
temp_rich_club_val
}
fn intermediate_rich_club_coef<'a, I, G1>(s_k: HashSet<VID>, views: I) -> f64
where
I: IntoIterator<Item = G1>,
G1: GraphViewOps<'a>,
{
let stable_edges = views
.into_iter()
.map(|g| {
let new_edges: HashSet<UndirEdge> = g
.subgraph(s_k.clone())
.edges()
.into_iter()
.filter(|e| e.src() != e.dst())
.map(|e| undir_edge(e.src().node, e.dst().node))
.collect();
new_edges
})
.reduce(|acc_edges, item_edges| acc_edges.intersection(&item_edges).cloned().collect());
match stable_edges {
Some(edges) => {
let poss_edges = (s_k.len() * (s_k.len() - 1)) / 2;
(edges.len() as f64) / (poss_edges as f64)
}
None => 0f64,
}
}
#[derive(Hash, Eq, PartialEq, Debug, Clone)]
pub struct UndirEdge {
src: VID,
dst: VID,
}
fn undir_edge<T: Into<VID>>(src: T, dst: T) -> UndirEdge {
let src_id: VID = src.into();
let dst_id: VID = dst.into();
UndirEdge {
src: min(src_id, dst_id),
dst: max(src_id, dst_id),
}
}