hypergraphx 0.0.5

A hypergraph library for Rust, based on the Python library of the same name.
Documentation
#![allow(dead_code)]
pub mod undirected;
pub use undirected::TemporalHypergraph as UndirectedTemporalGraph;
pub mod directed;
pub use directed::TemporalHypergraph as DirectedTemporalGraph;

/// Refer `TemporalBehaviour::Cooldown`.
#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)]
pub struct Cooldown {
    cooldown_begin: Option<usize>,
    cooldown_duration: usize,
    switch: bool,
}

impl Cooldown {
    /// This is what you use to consume (or activate, if switch is false) the edge.
    pub fn consume(&mut self, time: usize) -> bool {
        if let Some(cooldown_begin) = self.cooldown_begin {
            if time < (cooldown_begin + self.cooldown_duration) {
                return false;
            }
        }
        self.cooldown_begin = Some(time);
        true
    }
    fn is_active(&self, time: usize) -> bool {
        let out = {
            if let Some(cooldown_begin) = self.cooldown_begin {
                time < cooldown_begin || time >= (cooldown_begin + self.cooldown_duration)
            } else {
                false
            }
        };
        (out && !self.switch) || (!out && self.switch)
    }
}

/// This is a pretty important enum. It is what makes `TemporalHypergraph`s
/// temporal, allowing them to have edges that are active only at certain times.
/// Each instance is associated with an edge.
///
/// While the state of an edge (active or inactive) may vary throughout execution,
/// I'll try to ensure that the actual `TemporalBehaviour` enum cannot be changed
/// once created.
#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)]
pub enum TemporalBehaviour {
    /// A normal hyperedge. This is what the enum defaults to.
    Permanent,
    /// This edge *flips* exactly once. If it starts out active, it deactivates
    /// at time `offset`, and if it starts out inactive, it activates at time `offset`.
    ///
    /// Whether it starts out as active[true] or inactive[false] is determined by `switch`.
    Offset { offset: usize, switch: bool },
    /// This edge flips twice: once at `start` and once at `end`. Again, `switch`
    /// determines its which: if true, the edge is active only in this interval,
    /// and if false, it is active only outside it.
    ///
    /// Since all timestamps are integers, all intervals are inclusive.
    Interval {
        start: usize,
        end: usize,
        switch: bool,
    },
    /// This edge keeps flipping. Whether or not it is active depends on which part
    /// of its cycle you're in. The activity may be treated as a square wave which
    /// starts at time `offset`, with period `2*period`. Why the extra 2? Well,
    /// otherwise I'd have to constrain the value of period to be even, which is worse imo.
    ///
    /// Here, `switch` determines whether the edge starts out active.
    Periodic {
        offset: usize,
        period: usize,
        switch: bool,
    },
    /// The most complex one, in my opinion. This one is special in that you need
    /// mutable access to the hypergraph to change it. This edge can be *consume*d
    /// by any operation (with exclusive access to the temporal hypergraph) after
    /// which it remains inactive for the specified `cooldown` period.
    ///
    /// Changing `switch` inverts this behaviour, with it remaining active for
    /// the specified `cooldown` period. Of course, `switch` can't be *changed*
    /// during execution, only set once.
    Cooldown(Cooldown),
}

impl TemporalBehaviour {
    /// The one function you'd want. Does what it says.
    pub fn is_active(&self, time: usize) -> bool {
        match self {
            TemporalBehaviour::Permanent => true,
            TemporalBehaviour::Offset { offset, switch } => {
                let out = time >= *offset;
                (out && !switch) || (!out && *switch)
            }
            TemporalBehaviour::Interval { start, end, switch } => {
                let out = time >= *start && time < *end;
                (out && !switch) || (!out && *switch)
            }
            TemporalBehaviour::Periodic {
                offset,
                period,
                switch,
            } => {
                if time <= *offset {
                    return *switch;
                }
                return (time - offset) % (2 * period) <= *period;
            }
            TemporalBehaviour::Cooldown(cooldown) => cooldown.is_active(time),
        }
    }
}

impl Default for TemporalBehaviour {
    fn default() -> Self {
        TemporalBehaviour::Permanent
    }
}