1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! This module contains public interface for obtaining network properties.

use super::link::Weight;
use super::Model;
use super::Network;

use crate::CNErr;
use crate::Node;

use core::cell::{BorrowMutError, RefMut};
use rand::prelude::*;
use rustc_hash::FxHashMap as HashMap;

impl Network {
    /// Return the network size, ie. the total number of nodes.
    pub fn size(&self) -> usize {
        self.size
    }

    /// Return the model of the network
    pub fn model(&self) -> &Model {
        &self.model
    }

    /// Return the type of link weight
    pub fn weight(&self) -> &Weight {
        &self.weight
    }

    /// Try to borrow the network-local rng thread from it's `RefCell`. This is effectively to
    /// calling a `RefCell::try_borrow_mut()`.
    pub fn rng(&self) -> Result<RefMut<'_, impl RngCore>, BorrowMutError> {
        self.rng.try_borrow_mut()
    }

    /// Return iterator containing the node indexes. Use `nodes()` to iterate over the nodes
    /// themselves and `enumerated()` to use both indexes and values at once.
    pub fn indexes(&self) -> impl Iterator<Item = usize> + '_ {
        self.nodes.iter().map(|node| *node.index())
    }

    /// Return iterator containing the network nodes.
    pub fn nodes(&self) -> impl Iterator<Item = &Node> {
        self.nodes.iter()
    }

    /// Return iterator containing the (index, node) pairs.
    pub fn enumerated(&self) -> impl Iterator<Item = (usize, &Node)> + '_ {
        self.nodes.iter().map(|node| (*node.index(), node))
    }

    /// Returns immutable reference to a given node if it exists, `Err` if it does not.
    pub fn get(&self, index: usize) -> Result<&Node, CNErr> {
        if self.indexing_ok {
            // The node can be found at the specified index
            match self.nodes.get(index) {
                Some(node) => Ok(node),
                None => Err(CNErr::NoSuchNode(index)),
            }
        } else {
            // Binary search is required
            match self
                .nodes
                .binary_search_by_key(&index, |node| *node.index())
            {
                Ok(true_idx) => Ok(&self.nodes[true_idx]),
                Err(..) => Err(CNErr::NoSuchNode(index)),
            }
        }
    }

    /// Returns mutable reference to a given node if it exists, `Err` if it does not.
    pub(crate) fn get_mut(&mut self, index: usize) -> Result<&mut Node, CNErr> {
        if self.indexing_ok {
            // The node can be found at the specified index
            match self.nodes.get_mut(index) {
                Some(node) => Ok(node),
                None => Err(CNErr::NoSuchNode(index)),
            }
        } else {
            // Binary search is required
            match self
                .nodes
                .binary_search_by_key(&index, |node| *node.index())
            {
                Ok(true_idx) => Ok(&mut self.nodes[true_idx]),
                Err(..) => Err(CNErr::NoSuchNode(index)),
            }
        }
    }

    /// Return the number of connected nodes in the network, ie. ones that have at least one link
    pub fn size_connected(&self) -> usize {
        self.nodes()
            .filter(|&node| !node.links().is_empty())
            .count()
    }

    /// Return the total number of edges in the network.
    pub fn edges(&self) -> usize {
        self.total_deg() / 2
    }

    /// Calculate the arithmetical average of vertex degrees (ie. number of closest neighbors)
    /// in the network.
    pub fn avg_deg(&self) -> f64 {
        self.total_deg() as f64 / self.size as f64
    }

    /// Get the total degree of the network, ie. sum of degrees over all of the nodes.
    pub fn total_deg(&self) -> usize {
        self.nodes().map(|node| node.deg()).sum()
    }

    /// Return the degree distribution of the network as a `HashMap` of (degree, occurrences) pairs.
    pub fn deg_distr(&self) -> HashMap<usize, usize> {
        let mut bins: HashMap<usize, usize> = HashMap::default();
        for node in self.nodes() {
            let deg = node.deg();
            match bins.get_key_value(deg) {
                // If there already is such bin then increment it by 1
                Some((&key, &value)) => bins.insert(key, value + 1),
                // If there isn't create one with single occurrence
                None => bins.insert(*deg, 1),
            };
        }
        bins
    }
}