[][src]Crate net_ensembles

This lib is intended for scientific simulations

Example 1

use net_ensembles::{ErEnsembleC, EmptyNode, rand::SeedableRng};
use net_ensembles::traits::{WithGraph, SimpleSample, Dot};
use net_ensembles::{dot_options, dot_constants::*};
// Note: you might have to enable serde for rand_pcg
// to do that, write the following in your Cargo.toml:
// rand_pcg = { version = "*", features = ["serde1"]}
use rand_pcg::Pcg64;
use std::fs::File;

let rng = Pcg64::seed_from_u64(75676526);
// create graph with 50 vertices and target connectivity of 2.7
// using Pcg64 as random number generator
// NOTE: you can exchange `EmptyNode` with anything implementing the `Node` trait
let mut er = ErEnsembleC::<EmptyNode, _>::new(50, 2.7, rng);

// create dot file to visualize the graph
let mut f = File::create("50.dot")
                   .expect("Unable to create file");
// look at Dot trait
er.graph().dot(
     f,
     "" // you do not have to use dot_options
 ).unwrap();

// randomize the graph, uses SimpleSample trait
er.randomize();

let mut f = File::create("50_1.dot")
                   .expect("Unable to create file");
er.graph().dot_with_indices(
    f,
    dot_options!(NO_OVERLAP, MARGIN_0)
).unwrap();

// Note, you can also create a String this way:
let s =  er.graph().dot_string("");

To visualize, you can use something like

twopi 50.dot -Tpdf > 50.pdf
circo 50_1.dot -Tpdf > 50_1.pdf

You can also try some of the other roadmaps.

Example 2

You can also compute different measurable quantities, look at GenericGraph for more.

use net_ensembles::{traits::*, EmptyNode, ErEnsembleC};
use rand_pcg::Pcg64;
use rand::SeedableRng;

let rng = Pcg64::seed_from_u64(26);
// create graph with 50 vertices and target connectivity of 2.7
// using Pcg64 as random number generator
let er = ErEnsembleC::<EmptyNode, Pcg64>::new(50, 2.7, rng);
println!("Number of vertices: {}",      er.vertex_count());
println!("Number of edges: {}",         er.edge_count());
println!("Average degree: {}",          er.average_degree());
println!("connected components: {:?}",  er.connected_components());
println!("transitivity: {}",            er.transitivity());

Note: Also works for small-world ensemble, i.e. for SwEnsemble

Example 3

Simple sample for small-world ensemble

use net_ensembles::{SwEnsemble, EmptyNode};
use net_ensembles::traits::*; // I recommend always using this
use rand_pcg::Pcg64; //or whatever you want to use as rng
use rand::SeedableRng; // I use this to seed my rng, but you can use whatever
use std::fs::File;
use std::io::{BufWriter, Write};

let rng = Pcg64::seed_from_u64(1822);

// now create small-world ensemble with 100 nodes
// and a rewiring probability of 0.3 for each edge
let mut sw_ensemble = SwEnsemble::<EmptyNode, Pcg64>::new(100, 0.3, rng);

// setup file for writing
let f = File::create("simple_sample_sw.dat")
    .expect("Unable to create file");
let mut f = BufWriter::new(f);
f.write_all(b"#diameter bi_connect_max average_degree\n")
    .unwrap();

// simple sample for 10 steps
sw_ensemble.simple_sample(10,
    |ensemble|
    {
        let diameter = ensemble
            .diameter()
            .unwrap();

        let bi_connect_max = ensemble.graph().clone()
            .vertex_biconnected_components(false)[0];

        let average_degree = ensemble.graph()
            .average_degree();

        write!(f, "{} {} {}\n", diameter, bi_connect_max, average_degree)
            .unwrap();
    }
);

// or just collect this into a vector to print or do whatever
let vec = sw_ensemble.simple_sample_vec(10,
    |ensemble|
    {
        let diameter = ensemble.graph()
            .diameter()
            .unwrap();

        let transitivity = ensemble.graph()
            .transitivity();
        (diameter, transitivity)
    }
);
println!("{:?}", vec);

Example 4: Save and load

use net_ensembles::traits::*; // I recommend always using this
use serde_json;
use rand_pcg::Pcg64;
use net_ensembles::{ErEnsembleC, EmptyNode, rand::SeedableRng};
use std::fs::File;

let rng = Pcg64::seed_from_u64(95);
// create Erdős-Rényi ensemble
let ensemble = ErEnsembleC::<EmptyNode, Pcg64>::new(200, 3.1, rng);

#[cfg(feature = "serde_support")]
{
    // storing the ensemble in a file:

    let er_file = File::create("erC_save.dat")
          .expect("Unable to create file");

    // or serde_json::to_writer(er_file, &ensemble);
    serde_json::to_writer_pretty(er_file, &ensemble);

    // loading ensemble from file:

    let mut read = File::open("erC_save.dat")
        .expect("Unable to open file");

    let er: ErEnsembleC::<EmptyNode, Pcg64> = serde_json::from_reader(read).unwrap();
}

Example 5: Marcov Chain

use net_ensembles::{EmptyNode, ErEnsembleM, traits::*};
use rand_pcg::Pcg64;
use net_ensembles::rand::SeedableRng; // rand is reexported

// first create the ensemble
let rng = Pcg64::seed_from_u64(8745);
let mut e = ErEnsembleM::<EmptyNode, Pcg64>::new(30, 70, rng);

// ensure initial graph is connected
while !e.graph()
    .is_connected().unwrap() {
    e.randomize();
}

// Create marcov chain, e.g., of connected graphs
for _ in 0..100 {
    let steps = e.m_steps(10);

    // reject, if the resulting graph is not connected
    if !e.graph().is_connected().unwrap() {
        e.undo_steps_quiet(steps);
    }
    // mesure whatever you want
}

Example 6: Define your own Data

use net_ensembles::{traits::*, rand::SeedableRng, SwEnsemble};
use rand_pcg::Pcg64;

#[cfg(feature = "serde_support")]
use serde::{Serialize, Deserialize};

#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
pub enum SirState{
    Susceptible,
    Infective,
    Removed,
}

impl SirState {
    fn setState(&mut self, state: Self) {
        *self = state;
    }
}

impl Node for SirState {
    fn new_from_index(index: usize) -> Self {
        SirState::Susceptible
    }
}

// create the rng:
let rng = Pcg64::seed_from_u64(45);

let mut ensemble = SwEnsemble::<SirState, Pcg64>::new(10, 0.1, rng);

// you can access or change your additional information, e.g., at vertex 0
ensemble
    .at_mut(0)
    .setState(SirState::Infective);

// you can also iterate over your additional information:
let count = ensemble
    .contained_iter()
    .filter(|&state| *state == SirState::Susceptible)
    .count();
assert!(count == 9);

// or count how many Susceptible nodes are connected to a specific node, i.e., to node 0
let s_count = ensemble
    .contained_iter_neighbors(0)
    .filter(|&state| *state == SirState::Susceptible)
    .count();

println!("{}", s_count);

// or advance the states:
for state in ensemble.contained_iter_mut() {
    match *state {
        SirState::Infective     => { *state = SirState::Removed },
        _                       => { },
    };
}

Re-exports

pub use sw::SwEnsemble;
pub use sw_graph::SwGraph;
pub use er_m::ErEnsembleM;
pub use er_c::ErEnsembleC;
pub use graph::Graph;
pub use generic_graph::GenericGraph;
pub use example_nodes::EmptyNode;
pub use traits::*;
pub use iter::IterWrapper;
pub use step_structs::*;
pub use rand;

Modules

dot_constants

constants for dot options

er_c

Erdős-Rényi ensemble with target connectivity

er_m

Erdős-Rényi with constant number of edges

example_nodes

Example nodes implementing trait Node

generic_graph

Generic implementation for Topology

graph

Topology

iter

Contains definitions of a few iterators. Not All of them though.

sampling

For sampling ensembles

spacial

Spacial ensemble

step_structs

The structs returned by the mc steps

sw

Small-world ensemble

sw_graph

Topology for SwEnsemble

traits

You should use net_ensembles::traits::*

Macros

dot_options

You can chain/combine options with the dot_options! macro:

Structs

BAensemble

Implements a Barabási-Albert Graph ensemble

ConfigurationModel

Generate networks with a given degree distribution

Enums

ConfigurationModelStep

Markov step of configuration model

GraphErrors

Error messages

UndoStepErrorCM

Result of undoing a step via Markov Chain method of ConfigurationModel