hypergraphx 0.0.5

A hypergraph library for Rust, based on the Python library of the same name.
Documentation
use fixedbitset::FixedBitSet;
use hashbrown::HashSet;
use itertools::Itertools;

use crate::prelude::*;
pub trait GraphType: Send + Sync {}
pub struct Undirected;
impl GraphType for Undirected {}
pub struct Directed;
impl GraphType for Directed {}
#[cfg(feature = "multiplex")]
pub struct Multiplex;
#[cfg(feature = "multiplex")]
impl GraphType for Multiplex {}
#[cfg(feature = "temporal")]
pub struct Temporal;
#[cfg(feature = "temporal")]
impl GraphType for Temporal {}

/// Special trait for algorithm stuff.
/// It pained me physically to make two copies of an algorithm. This was originally made for the `costed_neighbours` method,
/// but more showed up.
///
/// So all of this.
pub trait CommonProperties<'a, T: GraphType>: GraphBasics<'a> {
    fn graph_type(&self) -> T;
    fn visit_map(&'a self) -> FixedBitSet {
        FixedBitSet::with_capacity(self.node_count())
    }
    fn is_empty(&'a self) -> bool {
        self.node_count() == 0 && self.edge_count() == 0
    }
    fn costed_neighbours<F, K>(
        &'a self,
        node_index: <Self as GraphBasics<'a>>::NodeIndex,
        cost: F,
    ) -> impl Iterator<Item = (K, HashSet<Self::NodeIndex>)>
    where
        F: Fn(<Self as GraphBasics<'a>>::EdgeIndex) -> K;

    fn costed_pairs<F, K>(
        &'a self,
        cost: F,
    ) -> (
        bool,
        impl Iterator<Item = (K, Vec<(Self::NodeIndex, Self::NodeIndex)>)>,
    )
    where
        F: Fn(<Self as GraphBasics<'a>>::EdgeIndex) -> K;

    fn neighbours_or_out_neighbours(
        &'a self,
        node_index: <Self as GraphBasics<'a>>::NodeIndex,
    ) -> Option<impl Iterator<Item = Self::NodeIndex>>;
    fn connects_nodes(
        &'a self,
        n1: Self::NodeIndex,
        n2: Self::NodeIndex,
        e: Self::EdgeIndex,
    ) -> bool;
    fn neighbours_with_edges(
        &'a self,
        node_index: <Self as GraphBasics<'a>>::NodeIndex,
    ) -> impl Iterator<Item = (Self::EdgeIndex, HashSet<Self::NodeIndex>)> {
        self.costed_neighbours(node_index, |e| e)
    }
}

impl<'a, T> CommonProperties<'a, Undirected> for T
where
    T: GraphBasics<'a> + GraphProperties<'a>,
{
    fn graph_type(&self) -> Undirected {
        Undirected
    }

    fn costed_neighbours<F, K>(
        &'a self,
        node_index: <Self as GraphBasics<'a>>::NodeIndex,
        cost: F,
    ) -> impl Iterator<Item = (K, HashSet<Self::NodeIndex>)>
    where
        F: Fn(<Self as GraphBasics<'a>>::EdgeIndex) -> K,
    {
        self.incident_edges(node_index)
            .unwrap()
            .into_iter()
            .map(move |edge_index| {
                let nodes = self.contained_nodes(edge_index).unwrap();
                let cost_value = cost(edge_index);
                (cost_value, nodes)
            })
    }

    fn is_empty(&'a self) -> bool {
        self.node_count() == 0 && self.edge_count() == 0
    }

    fn costed_pairs<F, K>(
        &'a self,
        cost: F,
    ) -> (
        bool,
        impl Iterator<Item = (K, Vec<(Self::NodeIndex, Self::NodeIndex)>)>,
    )
    where
        F: Fn(<Self as GraphBasics<'a>>::EdgeIndex) -> K,
    {
        (
            true,
            (0..self.edge_count()).map(move |e| {
                let nodes = self.contained_nodes(e.into()).unwrap();
                let cost_value = cost(e.into());
                (
                    cost_value,
                    nodes
                        .iter()
                        .map(|&n| n)
                        .tuple_combinations()
                        .collect::<Vec<_>>(),
                )
            }),
        )
    }

    fn neighbours_or_out_neighbours(
        &'a self,
        node_index: <Self as GraphBasics<'a>>::NodeIndex,
    ) -> Option<impl Iterator<Item = Self::NodeIndex>> {
        self.neighbours(node_index).map(|x| x.into_iter())
    }

    fn connects_nodes(
        &'a self,
        n1: Self::NodeIndex,
        n2: Self::NodeIndex,
        e: Self::EdgeIndex,
    ) -> bool {
        self.contained_nodes(e)
            .map(|v| v.contains(&n1) && v.contains(&n2))
            .unwrap_or(false)
    }
}

impl<'a, T> CommonProperties<'a, Directed> for T
where
    T: GraphBasics<'a> + DiGraphProperties<'a>,
{
    fn graph_type(&self) -> Directed {
        Directed
    }

    fn costed_neighbours<F, K>(
        &'a self,
        node_index: <Self as GraphBasics<'a>>::NodeIndex,
        cost: F,
    ) -> impl Iterator<Item = (K, HashSet<Self::NodeIndex>)>
    where
        F: Fn(<Self as GraphBasics<'a>>::EdgeIndex) -> K,
    {
        self.out_edges(node_index)
            .unwrap()
            .into_iter()
            .map(move |edge_index| {
                let nodes = self.target(edge_index).unwrap();
                let cost_value = cost(edge_index);
                (cost_value, nodes)
            })
    }

    fn costed_pairs<F, K>(
        &'a self,
        cost: F,
    ) -> (
        bool,
        impl Iterator<Item = (K, Vec<(Self::NodeIndex, Self::NodeIndex)>)>,
    )
    where
        F: Fn(<Self as GraphBasics<'a>>::EdgeIndex) -> K,
    {
        (
            false,
            (0..self.edge_count()).map(move |e| {
                let source = self.source(e.into()).unwrap();
                let target = self.target(e.into()).unwrap();
                let cost_value = cost(e.into());
                (
                    cost_value,
                    source
                        .iter()
                        .cartesian_product(target.iter())
                        .map(|(s, t)| (*s, *t))
                        .collect::<Vec<_>>(),
                )
            }),
        )
    }

    fn neighbours_or_out_neighbours(
        &'a self,
        node_index: <Self as GraphBasics<'a>>::NodeIndex,
    ) -> Option<impl Iterator<Item = Self::NodeIndex>> {
        self.out_neighbours(node_index).map(|x| x.into_iter())
    }

    fn connects_nodes(
        &'a self,
        n1: Self::NodeIndex,
        n2: Self::NodeIndex,
        e: Self::EdgeIndex,
    ) -> bool {
        self.source(e).map(|v| v.contains(&n1)).unwrap_or(false)
            && self.target(e).map(|v| v.contains(&n2)).unwrap_or(false)
    }
}