Skip to main content

example_2

Function example_2 

Source
pub fn example_2() -> Result<LatticePathLengths>
Expand description

Lesson 2: average path length of a lattice, before and after adding a few random edges.

Builds the undirected 30 × 30 square lattice with periodic boundaries in both dimensions (a torus: every vertex has degree 4) and computes its average shortest path length, 15 · 900 / 899 ≈ 15.0167. Then it seeds the thread’s default random number generator with 42, draws 20 uniform vertex ids in 0..900 (pairing them up as ten edges; in general such draws may include loops and multi-edges, although with seed 42 they do not), adds them to the lattice and computes the average path length again, ≈ 11.8142: ten random shortcuts reduce distances by more than 20%.

It translates examples/tutorial/tutorial2.c of the second lesson, drawing the random numbers in the same order, so that the results are exactly those printed by the C program with igraph 1.0.1.

§C code

#include <igraph.h>

int main(void) {
    igraph_t graph;
    igraph_vector_int_t dimvector;
    igraph_vector_int_t edges;
    igraph_vector_bool_t periodic;
    igraph_real_t avg_path_len;

    /* Initialize the library. */
    igraph_setup();

    igraph_vector_int_init(&dimvector, 2);
    VECTOR(dimvector)[0] = 30;
    VECTOR(dimvector)[1] = 30;

    igraph_vector_bool_init(&periodic, 2);
    igraph_vector_bool_fill(&periodic, true);
    igraph_square_lattice(&graph, &dimvector, 0, IGRAPH_UNDIRECTED,
                          /* mutual= */ false, &periodic);

    igraph_average_path_length(&graph, NULL, &avg_path_len, NULL,
                               IGRAPH_UNDIRECTED, /* unconn= */ true);
    printf("Average path length (lattice):            %g\n", (double) avg_path_len);

    /* Seed the RNG to ensure identical results across runs. */
    igraph_rng_seed(igraph_rng_default(), 42);

    igraph_vector_int_init(&edges, 20);
    for (igraph_int_t i = 0; i < igraph_vector_int_size(&edges); i++) {
        VECTOR(edges)[i] = RNG_INTEGER(0, igraph_vcount(&graph) - 1);
    }

    igraph_add_edges(&graph, &edges, NULL);
    igraph_average_path_length(&graph, NULL, &avg_path_len, NULL,
                               IGRAPH_UNDIRECTED, /* unconn= */ true);
    printf("Average path length (randomized lattice): %g\n", (double) avg_path_len);

    igraph_vector_bool_destroy(&periodic);
    igraph_vector_int_destroy(&dimvector);
    igraph_vector_int_destroy(&edges);
    igraph_destroy(&graph);

    return 0;
}

§Rust translation

The body of this function, step by step: plain arrays replace the igraph_vector_*_t objects, and nothing needs to be destroyed.

use igraph::prelude::*;

let mut graph = Graph::square_lattice(
    &[30, 30],          // dimensions
    0,                  // nei: only direct neighbors
    false,              // undirected
    false,              // mutual (directed graphs only)
    Some(&[true, true]), // periodic in both dimensions
)?;

let lattice = graph.average_path_length(None, false, true)?;
println!("Average path length (lattice):            {lattice}");
assert!((lattice - 15.0 * 900.0 / 899.0).abs() < 1e-12);

// Seed the RNG to ensure identical results across runs.
rng::seed(42)?;

let n = graph.vcount() as i64;
let edges: Vec<i64> = (0..20).map(|_| rng::integer(0, n - 1)).collect();

graph.add_edges_from_vector(&edges)?;
let randomized = graph.average_path_length(None, false, true)?;
println!("Average path length (randomized lattice): {randomized}");
assert!((randomized - 11.814163885799037).abs() < 1e-12);

// ... which is what `example_2` returns.
let res = igraph::tutorial::example_2()?;
assert_eq!((res.lattice, res.randomized), (lattice, randomized));
assert_eq!(res.to_string(), "\
Average path length (lattice):            15.0167
Average path length (randomized lattice): 11.8142");

§Errors

Propagates the errors of the igraph calls (none are expected).