graphrs 0.11.16

graphrs is a Rust package for the creation, manipulation and analysis of graphs.
Documentation
use super::utility::{get_adjacent_nodes_without, get_normalized_edge_weight};
use crate::Graph;
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
use std::collections::HashSet;
use std::fmt::Display;
use std::hash::Hash;

pub struct DirectedWeightedTrianglesAndDegree<T> {
    pub node_name: T,
    pub total_degree: usize,
    pub reciprocal_degree: usize,
    pub directed_triangles: f64,
}

pub fn get_directed_weighted_triangles_and_degrees<T, A>(
    graph: &Graph<T, A>,
    node_names: Option<&[T]>,
) -> Vec<DirectedWeightedTrianglesAndDegree<T>>
where
    T: Hash + Eq + Clone + Ord + Display + Send + Sync,
    A: Clone + Send + Sync,
{
    let edges = graph.get_all_edges();
    let max_weight = match edges.is_empty() {
        true => 1.0,
        false => edges.iter().map(|e| e.weight).reduce(f64::max).unwrap(),
    };
    let ns: Vec<T> = match node_names {
        None => graph
            .get_all_nodes()
            .into_iter()
            .map(|n| n.name.clone())
            .collect(),
        Some(names) => names.to_vec(),
    };
    ns.into_par_iter()
        .map(|i| {
            let ipreds = get_adjacent_nodes_without(graph, &i, true);
            let isuccs = get_adjacent_nodes_without(graph, &i, false);
            let directed_triangles_ipreds =
                get_all_directed_triangles(&i, &ipreds, &isuccs, true, graph, &max_weight);
            let directed_triangles_isuccs =
                get_all_directed_triangles(&i, &ipreds, &isuccs, false, graph, &max_weight);
            let total_degree = ipreds.len() + isuccs.len();
            let reciprocal_degree = ipreds.intersection(&isuccs).count();
            DirectedWeightedTrianglesAndDegree {
                node_name: i,
                total_degree,
                reciprocal_degree,
                directed_triangles: directed_triangles_ipreds + directed_triangles_isuccs,
            }
        })
        .collect()
}

#[inline]
#[rustfmt::skip]
fn get_all_directed_triangles<T, A>(
    i: &T,
    ipreds: &HashSet<T>,
    isuccs: &HashSet<T>,
    iter_preds: bool,
    graph: &Graph<T, A>,
    max_weight: &f64,
) -> f64
where
    T: Hash + Eq + Clone + Ord + Display + Send + Sync,
    A: Clone + Send + Sync,
{
    let to_iter = match iter_preds { true => ipreds, false => isuccs };
    let wt = |u: &T, v: &T| get_normalized_edge_weight(u, v, max_weight, graph);
    to_iter
        .iter()
        .map(|j: &T| {
            let (_i, _j) = match iter_preds { true => (j, i), false => (i, j) };
            let jpreds = get_adjacent_nodes_without(graph, j, true);
            let jsuccs = get_adjacent_nodes_without(graph, j, false);
            let directed_triangles: f64 = 
                ipreds.intersection(&jpreds).into_iter().map(|k| (wt(_i, _j) * wt(k, i) * wt(k, j)).cbrt()).sum::<f64>() +
                ipreds.intersection(&jsuccs).into_iter().map(|k| (wt(_i, _j) * wt(k, i) * wt(j, k)).cbrt()).sum::<f64>() +
                isuccs.intersection(&jpreds).into_iter().map(|k| (wt(_i, _j) * wt(i, k) * wt(k, j)).cbrt()).sum::<f64>() +
                isuccs.intersection(&jsuccs).into_iter().map(|k| (wt(_i, _j) * wt(i, k) * wt(j, k)).cbrt()).sum::<f64>();
            directed_triangles
        })
        .sum::<f64>()
}