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).