#![allow(clippy::float_cmp)]
use std::hash::Hash;
use petgraph::data::{Build, Create};
use petgraph::visit::{Data, EdgeRef, GraphBase, GraphProp, IntoEdgeReferences, NodeIndexable};
use rand::distributions::{Distribution, Uniform};
use rand::prelude::*;
use rand_pcg::Pcg64;
use super::InvalidInputError;
pub fn gnp_random_graph<G, T, F, H, M>(
num_nodes: usize,
probability: f64,
seed: Option<u64>,
mut default_node_weight: F,
mut default_edge_weight: H,
) -> Result<G, InvalidInputError>
where
G: Build + Create + Data<NodeWeight = T, EdgeWeight = M> + NodeIndexable + GraphProp,
F: FnMut() -> T,
H: FnMut() -> M,
G::NodeId: Eq + Hash,
{
if num_nodes == 0 {
return Err(InvalidInputError {});
}
let mut rng: Pcg64 = match seed {
Some(seed) => Pcg64::seed_from_u64(seed),
None => Pcg64::from_entropy(),
};
let mut graph = G::with_capacity(num_nodes, num_nodes);
let directed = graph.is_directed();
for _ in 0..num_nodes {
graph.add_node(default_node_weight());
}
if !(0.0..=1.0).contains(&probability) {
return Err(InvalidInputError {});
}
if probability > 0.0 {
if (probability - 1.0).abs() < std::f64::EPSILON {
for u in 0..num_nodes {
let start_node = if directed { 0 } else { u + 1 };
for v in start_node..num_nodes {
if !directed || u != v {
let u_index = graph.from_index(u);
let v_index = graph.from_index(v);
graph.add_edge(u_index, v_index, default_edge_weight());
}
}
}
} else {
let mut v: isize = if directed { 0 } else { 1 };
let mut w: isize = -1;
let num_nodes: isize = num_nodes as isize;
let lp: f64 = (1.0 - probability).ln();
let between = Uniform::new(0.0, 1.0);
while v < num_nodes {
let random: f64 = between.sample(&mut rng);
let lr: f64 = (1.0 - random).ln();
let ratio: isize = (lr / lp) as isize;
w = w + 1 + ratio;
if directed {
if v == w {
w += 1;
}
}
while v < num_nodes && ((directed && num_nodes <= w) || (!directed && v <= w)) {
w -= v;
v += 1;
if directed && v == w {
w -= v;
v += 1;
}
}
if v < num_nodes {
let v_index = graph.from_index(v as usize);
let w_index = graph.from_index(w as usize);
graph.add_edge(v_index, w_index, default_edge_weight());
}
}
}
}
Ok(graph)
}
pub fn gnm_random_graph<G, T, F, H, M>(
num_nodes: usize,
num_edges: usize,
seed: Option<u64>,
mut default_node_weight: F,
mut default_edge_weight: H,
) -> Result<G, InvalidInputError>
where
G: GraphProp + Build + Create + Data<NodeWeight = T, EdgeWeight = M> + NodeIndexable,
F: FnMut() -> T,
H: FnMut() -> M,
for<'b> &'b G: GraphBase<NodeId = G::NodeId> + IntoEdgeReferences,
G::NodeId: Eq + Hash,
{
if num_nodes == 0 {
return Err(InvalidInputError {});
}
fn find_edge<G>(graph: &G, source: usize, target: usize) -> bool
where
G: GraphBase + NodeIndexable,
for<'b> &'b G: GraphBase<NodeId = G::NodeId> + IntoEdgeReferences,
{
let mut found = false;
for edge in graph.edge_references() {
if graph.to_index(edge.source()) == source && graph.to_index(edge.target()) == target {
found = true;
break;
}
}
found
}
let mut rng: Pcg64 = match seed {
Some(seed) => Pcg64::seed_from_u64(seed),
None => Pcg64::from_entropy(),
};
let mut graph = G::with_capacity(num_nodes, num_edges);
let directed = graph.is_directed();
for _ in 0..num_nodes {
graph.add_node(default_node_weight());
}
let div_by = if directed { 1 } else { 2 };
if num_edges >= num_nodes * (num_nodes - 1) / div_by {
for u in 0..num_nodes {
let start_node = if directed { 0 } else { u + 1 };
for v in start_node..num_nodes {
if !directed || u != v {
let u_index = graph.from_index(u);
let v_index = graph.from_index(v);
graph.add_edge(u_index, v_index, default_edge_weight());
}
}
}
} else {
let mut created_edges: usize = 0;
let between = Uniform::new(0, num_nodes);
while created_edges < num_edges {
let u = between.sample(&mut rng);
let v = between.sample(&mut rng);
let u_index = graph.from_index(u);
let v_index = graph.from_index(v);
if u != v && !find_edge(&graph, u, v) {
graph.add_edge(u_index, v_index, default_edge_weight());
created_edges += 1;
}
}
}
Ok(graph)
}
#[inline]
fn pnorm(x: f64, p: f64) -> f64 {
if p == 1.0 || p == std::f64::INFINITY {
x.abs()
} else if p == 2.0 {
x * x
} else {
x.abs().powf(p)
}
}
fn distance(x: &[f64], y: &[f64], p: f64) -> f64 {
let it = x.iter().zip(y.iter()).map(|(xi, yi)| pnorm(xi - yi, p));
if p == std::f64::INFINITY {
it.fold(-1.0, |max, x| if x > max { x } else { max })
} else {
it.sum()
}
}
pub fn random_geometric_graph<G, H, M>(
num_nodes: usize,
radius: f64,
dim: usize,
pos: Option<Vec<Vec<f64>>>,
p: f64,
seed: Option<u64>,
mut default_edge_weight: H,
) -> Result<G, InvalidInputError>
where
G: GraphBase + Build + Create + Data<NodeWeight = Vec<f64>, EdgeWeight = M> + NodeIndexable,
H: FnMut() -> M,
for<'b> &'b G: GraphBase<NodeId = G::NodeId> + IntoEdgeReferences,
G::NodeId: Eq + Hash,
{
if num_nodes == 0 {
return Err(InvalidInputError {});
}
let mut rng: Pcg64 = match seed {
Some(seed) => Pcg64::seed_from_u64(seed),
None => Pcg64::from_entropy(),
};
let mut graph = G::with_capacity(num_nodes, num_nodes);
let radius_p = pnorm(radius, p);
let dist = Uniform::new(0.0, 1.0);
let pos = pos.unwrap_or_else(|| {
(0..num_nodes)
.map(|_| (0..dim).map(|_| dist.sample(&mut rng)).collect())
.collect()
});
if num_nodes != pos.len() {
return Err(InvalidInputError {});
}
for pval in pos.iter() {
graph.add_node(pval.clone());
}
for u in 0..(num_nodes - 1) {
for v in (u + 1)..num_nodes {
if distance(&pos[u], &pos[v], p) < radius_p {
graph.add_edge(
graph.from_index(u),
graph.from_index(v),
default_edge_weight(),
);
}
}
}
Ok(graph)
}
#[cfg(test)]
mod tests {
use crate::generators::InvalidInputError;
use crate::generators::{gnm_random_graph, gnp_random_graph, random_geometric_graph};
use crate::petgraph;
#[test]
fn test_gnp_random_graph_directed() {
let g: petgraph::graph::DiGraph<(), ()> =
gnp_random_graph(20, 0.5, Some(10), || (), || ()).unwrap();
assert_eq!(g.node_count(), 20);
assert_eq!(g.edge_count(), 104);
}
#[test]
fn test_gnp_random_graph_directed_empty() {
let g: petgraph::graph::DiGraph<(), ()> =
gnp_random_graph(20, 0.0, None, || (), || ()).unwrap();
assert_eq!(g.node_count(), 20);
assert_eq!(g.edge_count(), 0);
}
#[test]
fn test_gnp_random_graph_directed_complete() {
let g: petgraph::graph::DiGraph<(), ()> =
gnp_random_graph(20, 1.0, None, || (), || ()).unwrap();
assert_eq!(g.node_count(), 20);
assert_eq!(g.edge_count(), 20 * (20 - 1));
}
#[test]
fn test_gnp_random_graph_undirected() {
let g: petgraph::graph::UnGraph<(), ()> =
gnp_random_graph(20, 0.5, Some(10), || (), || ()).unwrap();
assert_eq!(g.node_count(), 20);
assert_eq!(g.edge_count(), 105);
}
#[test]
fn test_gnp_random_graph_undirected_empty() {
let g: petgraph::graph::UnGraph<(), ()> =
gnp_random_graph(20, 0.0, None, || (), || ()).unwrap();
assert_eq!(g.node_count(), 20);
assert_eq!(g.edge_count(), 0);
}
#[test]
fn test_gnp_random_graph_undirected_complete() {
let g: petgraph::graph::UnGraph<(), ()> =
gnp_random_graph(20, 1.0, None, || (), || ()).unwrap();
assert_eq!(g.node_count(), 20);
assert_eq!(g.edge_count(), 20 * (20 - 1) / 2);
}
#[test]
fn test_gnp_random_graph_error() {
match gnp_random_graph::<petgraph::graph::DiGraph<(), ()>, (), _, _, ()>(
0,
3.0,
None,
|| (),
|| (),
) {
Ok(_) => panic!("Returned a non-error"),
Err(e) => assert_eq!(e, InvalidInputError),
};
}
#[test]
fn test_gnm_random_graph_directed() {
let g: petgraph::graph::DiGraph<(), ()> =
gnm_random_graph(20, 100, None, || (), || ()).unwrap();
assert_eq!(g.node_count(), 20);
assert_eq!(g.edge_count(), 100);
}
#[test]
fn test_gnm_random_graph_directed_empty() {
let g: petgraph::graph::DiGraph<(), ()> =
gnm_random_graph(20, 0, None, || (), || ()).unwrap();
assert_eq!(g.node_count(), 20);
assert_eq!(g.edge_count(), 0);
}
#[test]
fn test_gnm_random_graph_directed_complete() {
let g: petgraph::graph::DiGraph<(), ()> =
gnm_random_graph(20, 20 * (20 - 1), None, || (), || ()).unwrap();
assert_eq!(g.node_count(), 20);
assert_eq!(g.edge_count(), 20 * (20 - 1));
}
#[test]
fn test_gnm_random_graph_directed_max_edges() {
let n = 20;
let max_m = n * (n - 1);
let g: petgraph::graph::DiGraph<(), ()> =
gnm_random_graph(n, max_m, None, || (), || ()).unwrap();
assert_eq!(g.node_count(), n);
assert_eq!(g.edge_count(), max_m);
let g: petgraph::graph::DiGraph<(), ()> =
gnm_random_graph(n, max_m + 1, None, || (), || ()).unwrap();
assert_eq!(g.node_count(), n);
assert_eq!(g.edge_count(), max_m);
let g: petgraph::graph::DiGraph<(), ()> =
gnm_random_graph(n, max_m, Some(55), || (), || ()).unwrap();
assert_eq!(g.node_count(), n);
assert_eq!(g.edge_count(), max_m);
}
#[test]
fn test_gnm_random_graph_error() {
match gnm_random_graph::<petgraph::graph::DiGraph<(), ()>, (), _, _, ()>(
0,
0,
None,
|| (),
|| (),
) {
Ok(_) => panic!("Returned a non-error"),
Err(e) => assert_eq!(e, InvalidInputError),
};
}
#[test]
fn test_random_geometric_empty() {
let g: petgraph::graph::UnGraph<Vec<f64>, ()> =
random_geometric_graph(20, 0.0, 2, None, 2.0, None, || ()).unwrap();
assert_eq!(g.node_count(), 20);
assert_eq!(g.edge_count(), 0);
}
#[test]
fn test_random_geometric_complete() {
let g: petgraph::graph::UnGraph<Vec<f64>, ()> =
random_geometric_graph(10, 1.42, 2, None, 2.0, None, || ()).unwrap();
assert_eq!(g.node_count(), 10);
assert_eq!(g.edge_count(), 45);
}
#[test]
fn test_random_geometric_bad_num_nodes() {
match random_geometric_graph::<petgraph::graph::UnGraph<Vec<f64>, ()>, _, ()>(
0,
1.0,
2,
None,
2.0,
None,
|| (),
) {
Ok(_) => panic!("Returned a non-error"),
Err(e) => assert_eq!(e, InvalidInputError),
};
}
#[test]
fn test_random_geometric_bad_pos() {
match random_geometric_graph::<petgraph::graph::UnGraph<Vec<f64>, ()>, _, ()>(
3,
0.15,
3,
Some(vec![vec![0.5, 0.5]]),
2.0,
None,
|| (),
) {
Ok(_) => panic!("Returned a non-error"),
Err(e) => assert_eq!(e, InvalidInputError),
};
}
}