use std::collections::HashMap;
use std::hash::Hash;
use super::graph::Graph;
use super::Vertex;
use crate::utils::{mean, standard_deviation};
use distances::Number;
use crate::Cluster;
pub type ClusterScores<'a, U> = HashMap<&'a Vertex<U>, f64>;
pub type InstanceScores = HashMap<usize, f64>;
pub trait GraphScorer<'a, U: Number>: Hash {
fn call(&self, graph: &'a Graph<'a, U>) -> Result<(ClusterScores<'a, U>, Vec<f64>), String> {
let cluster_scores = {
let scores = self.score_graph(graph)?;
let mut cluster_scores: ClusterScores<'a, U> = scores;
if self.normalize_on_clusters() {
let (clusters, scores): (Vec<_>, Vec<_>) = cluster_scores.into_iter().unzip();
cluster_scores = clusters
.into_iter()
.zip(crate::utils::normalize_1d(
&scores,
mean(&scores),
standard_deviation(&scores),
))
.collect();
}
cluster_scores
};
let instance_scores = {
let mut instance_scores = self.inherit_scores(&cluster_scores);
if !self.normalize_on_clusters() {
let (indices, scores): (Vec<_>, Vec<_>) = instance_scores.into_iter().unzip();
instance_scores = indices
.into_iter()
.zip(crate::utils::normalize_1d(
&scores,
mean(&scores),
standard_deviation(&scores),
))
.collect();
}
instance_scores
};
let scores_array = self.ordered_scores(&instance_scores);
Ok((cluster_scores, scores_array))
}
fn name(&self) -> &str;
fn short_name(&self) -> &str;
fn normalize_on_clusters(&self) -> bool;
fn score_graph(&self, graph: &'a Graph<'a, U>) -> Result<ClusterScores<'a, U>, String>;
fn inherit_scores(&self, scores: &ClusterScores<U>) -> InstanceScores {
scores
.iter()
.flat_map(|(&c, &s)| c.indices().map(move |i| (i, s)))
.collect()
}
fn ordered_scores(&self, scores: &InstanceScores) -> Vec<f64> {
let mut scores: Vec<_> = scores.iter().map(|(&i, &s)| (i, s)).collect();
scores.sort_by_key(|(i, _)| *i);
let (_, scores): (Vec<_>, Vec<f64>) = scores.into_iter().unzip();
scores
}
}
pub struct ClusterCardinality;
impl Hash for ClusterCardinality {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
"cluster_cardinality".hash(state);
}
}
impl<'a, U: Number> GraphScorer<'a, U> for ClusterCardinality {
fn name(&self) -> &str {
"cluster_cardinality"
}
fn short_name(&self) -> &str {
"cc"
}
fn normalize_on_clusters(&self) -> bool {
true
}
fn score_graph(&self, graph: &'a Graph<'a, U>) -> Result<ClusterScores<'a, U>, String> {
let scores = graph
.ordered_clusters()
.iter()
.map(|&c| (c, -c.cardinality().as_f64()))
.collect();
Ok(scores)
}
}
pub struct ComponentCardinality;
impl Hash for ComponentCardinality {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
"component_cardinality".hash(state);
}
}
impl<'a, U: Number> GraphScorer<'a, U> for ComponentCardinality {
fn name(&self) -> &str {
"component_cardinality"
}
fn short_name(&self) -> &str {
"sc"
}
fn normalize_on_clusters(&self) -> bool {
true
}
fn score_graph(&self, graph: &'a Graph<'a, U>) -> Result<ClusterScores<'a, U>, String> {
let scores = graph
.find_component_clusters()
.iter()
.flat_map(|clusters| {
let score = -clusters.len().as_f64();
clusters.iter().map(move |&c| (c, score))
})
.collect();
Ok(scores)
}
}
pub struct VertexDegree;
impl Hash for VertexDegree {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
"vertex_degree".hash(state);
}
}
impl<'a, U: Number> GraphScorer<'a, U> for VertexDegree {
fn name(&self) -> &str {
"vertex_degree"
}
fn short_name(&self) -> &str {
"vd"
}
fn normalize_on_clusters(&self) -> bool {
true
}
#[allow(clippy::cast_precision_loss)]
fn score_graph(&self, graph: &'a Graph<'a, U>) -> Result<ClusterScores<'a, U>, String> {
let scores: Result<ClusterScores<'a, U>, String> = graph
.ordered_clusters()
.iter()
.map(|&c| graph.vertex_degree(c).map(|degree| (c, -(degree as f64))))
.collect();
scores
}
}
pub struct ParentCardinality<'a, U: Number> {
#[allow(dead_code)]
root: &'a Vertex<U>,
#[allow(dead_code)]
weight: Box<dyn (Fn(usize) -> f64) + Send + Sync>,
}
impl<'a, U: Number> ParentCardinality<'a, U> {
pub fn new(root: &'a Vertex<U>) -> Self {
let weight = Box::new(|d: usize| 1. / (d.as_f64()).sqrt());
Self { root, weight }
}
pub fn ancestry(&self, _c: &'a Vertex<U>) -> Vec<&'a Vertex<U>> {
todo!()
}
}
impl<'a, U: Number> Hash for ParentCardinality<'a, U> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
"parent_cardinality".hash(state);
}
}
impl<'a, U: Number> GraphScorer<'a, U> for ParentCardinality<'a, U> {
fn name(&self) -> &str {
"parent_cardinality"
}
fn short_name(&self) -> &str {
"pc"
}
fn normalize_on_clusters(&self) -> bool {
todo!()
}
fn score_graph(&self, graph: &'a Graph<'a, U>) -> Result<ClusterScores<'a, U>, String> {
let scores = graph
.ordered_clusters()
.iter()
.map(|&c| {
let ancestry = self.ancestry(c);
let score: f64 = ancestry
.iter()
.skip(1)
.zip(ancestry.iter())
.enumerate()
.map(|(i, (child, parent))| {
(self.weight)(i + 1) * parent.cardinality().as_f64() / child.cardinality().as_f64()
})
.sum();
(c, -score)
})
.collect();
Ok(scores)
}
}
pub struct GraphNeighborhood {
#[allow(dead_code)]
eccentricity_fraction: f64,
}
impl GraphNeighborhood {
#[must_use]
pub const fn new(eccentricity_fraction: f64) -> Self {
Self { eccentricity_fraction }
}
#[allow(dead_code)]
fn num_steps<'a, U: Number>(&self, _graph: &'a Graph<'a, U>, _c: &'a Vertex<U>) -> usize {
todo!()
}
}
impl Hash for GraphNeighborhood {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
"graph_neighborhood".hash(state);
}
}
impl<'a, U: Number> GraphScorer<'a, U> for GraphNeighborhood {
fn name(&self) -> &str {
"graph_neighborhood"
}
fn short_name(&self) -> &str {
"gn"
}
fn normalize_on_clusters(&self) -> bool {
todo!()
}
fn score_graph(&self, _graph: &'a Graph<'a, U>) -> Result<ClusterScores<'a, U>, String> {
todo!()
}
}
pub struct StationaryProbabilities {
#[allow(dead_code)]
num_steps: usize,
}
impl Hash for StationaryProbabilities {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
"stationary_probabilities".hash(state);
}
}
impl StationaryProbabilities {
#[must_use]
pub const fn new(num_steps: usize) -> Self {
Self { num_steps }
}
}
impl<'a, U: Number> GraphScorer<'a, U> for StationaryProbabilities {
fn name(&self) -> &str {
"stationary_probabilities"
}
fn short_name(&self) -> &str {
"sp"
}
fn normalize_on_clusters(&self) -> bool {
todo!()
}
#[allow(unused_variables)]
fn score_graph(&self, graph: &'a Graph<U>) -> Result<ClusterScores<'a, U>, String> {
todo!()
}
}