swactor/crates/simulation/src/topology.rs

95 lines
3 KiB
Rust
Raw Normal View History

/// Network topology shapes for simulation.
#[derive(Debug, Clone)]
pub enum Topology {
/// Each node gossips to the next; last gossips to first.
Ring,
/// Node 0 is the hub; all others gossip to/from it.
Star,
/// Every node gossips to every other node.
FullMesh,
/// Unidirectional chain: 0→1→2→…→(n-1).
Chain,
/// Two halves with no cross-links (healed later via `heal_after_round`).
Partitioned,
}
impl Topology {
/// Compute abstract index-pair edges for `num_nodes` nodes.
pub fn edges(&self, num_nodes: usize) -> Vec<(usize, usize)> {
let n = num_nodes;
let mut edges = Vec::new();
match self {
Topology::Ring => {
for i in 0..n {
edges.push((i, (i + 1) % n));
}
}
Topology::Star => {
for i in 1..n {
edges.push((0, i));
edges.push((i, 0));
}
}
Topology::FullMesh => {
for i in 0..n {
for j in 0..n {
if i != j {
edges.push((i, j));
}
}
}
}
Topology::Chain => {
for i in 0..n.saturating_sub(1) {
edges.push((i, i + 1));
}
}
Topology::Partitioned => {
let half = n / 2;
for i in 0..half {
for j in 0..half {
if i != j {
edges.push((i, j));
}
}
}
for i in half..n {
for j in half..n {
if i != j {
edges.push((i, j));
}
}
}
}
}
edges
}
/// Partition healing edges: bidirectional links between the two halves.
///
/// Connects up to 3 evenly-spaced node pairs across the partition boundary.
/// A single bridge link is unreliable under random peer selection: with half=50,
/// there is only a 1/50 chance per round that the bridge node gossips across,
/// giving a ~1.7% probability of zero crossings in 200 rounds.
pub fn heal_edges(&self, num_nodes: usize) -> Vec<(usize, usize)> {
if !matches!(self, Topology::Partitioned) {
return Vec::new();
}
let half = num_nodes / 2;
if half == 0 || half >= num_nodes {
return Vec::new();
}
let right = num_nodes - half;
let num_bridges = half.min(3);
let mut edges = Vec::with_capacity(num_bridges * 2);
for b in 0..num_bridges {
let a_node = b * half / num_bridges;
let b_node = half + b * right / num_bridges;
edges.push((a_node, b_node));
edges.push((b_node, a_node));
}
edges
}
}