use crate::fixed::{reconstruct, Graph, PR_SCALE_PUB as SCALE};
use inf_value::{Tuple, Value};
use std::collections::{BTreeMap, BTreeSet, VecDeque};
pub fn run(
algo: &str,
g: &Graph,
params: &[(String, i64)],
pos: &[Value],
) -> Result<Vec<Tuple>, String> {
let p = |k: &str, d: i64| params.iter().find(|(n, _)| n == k).map(|(_, v)| *v).unwrap_or(d);
let at = |i: usize| pos.get(i).cloned().ok_or_else(|| format!("`{algo}` missing argument {i}"));
match algo {
"ClosenessCentrality" => Ok(closeness(g)),
"BetweennessCentrality" => Ok(betweenness(g)),
"LabelPropagation" => Ok(label_propagation(g, p("iters", 10))),
"ClusteringCoefficients" => Ok(clustering(g)),
"CommunityDetectionLouvain" => Ok(louvain(g)),
"MinimumSpanningForestKruskal" | "MinimumSpanningTreePrim" => Ok(kruskal(g)),
"ShortestPathAStar" => Ok(match dijkstra(g, &at(0)?, &at(1)?, &BTreeSet::new(), &BTreeSet::new()) {
Some((path, cost)) => vec![vec![Value::List(path), Value::Int(cost)]],
None => vec![],
}),
"KShortestPathYen" => Ok(yen(g, &at(0)?, &at(1)?, p("k", 3).max(1) as usize)),
"RandomWalk" => Ok(random_walk(g, &at(0)?, p("steps", 10), p("times", 1), p("seed", 1) as u64)),
other => Err(format!("fixed rule `{other}` is not implemented in the reference engine")),
}
}
fn bfs_dist(g: &Graph, src: &Value) -> BTreeMap<Value, i64> {
let mut dist = BTreeMap::new();
let mut q = VecDeque::new();
dist.insert(src.clone(), 0);
q.push_back(src.clone());
while let Some(u) = q.pop_front() {
let d = dist[&u];
for (v, _) in g.neighbors(&u) {
if !dist.contains_key(v) {
dist.insert(v.clone(), d + 1);
q.push_back(v.clone());
}
}
}
dist
}
fn closeness(g: &Graph) -> Vec<Tuple> {
g.nodes
.iter()
.map(|s| {
let dist = bfs_dist(g, s);
let sum: i64 = dist.values().sum();
let reach = (dist.len() as i64) - 1;
let c = if sum > 0 { SCALE * reach / sum } else { 0 };
vec![s.clone(), Value::Int(c)]
})
.collect()
}
fn betweenness(g: &Graph) -> Vec<Tuple> {
let mut betw: BTreeMap<Value, i64> = g.nodes.iter().map(|v| (v.clone(), 0)).collect();
for s in &g.nodes {
let mut stack = Vec::new();
let mut pred: BTreeMap<Value, Vec<Value>> = BTreeMap::new();
let mut sigma: BTreeMap<Value, i64> = g.nodes.iter().map(|v| (v.clone(), 0)).collect();
let mut dist: BTreeMap<Value, i64> = g.nodes.iter().map(|v| (v.clone(), -1)).collect();
sigma.insert(s.clone(), 1);
dist.insert(s.clone(), 0);
let mut q = VecDeque::new();
q.push_back(s.clone());
while let Some(v) = q.pop_front() {
stack.push(v.clone());
for (w, _) in g.neighbors(&v) {
if dist[w] < 0 {
dist.insert(w.clone(), dist[&v] + 1);
q.push_back(w.clone());
}
if dist[w] == dist[&v] + 1 {
*sigma.get_mut(w).unwrap() += sigma[&v];
pred.entry(w.clone()).or_default().push(v.clone());
}
}
}
let mut delta: BTreeMap<Value, i64> = g.nodes.iter().map(|v| (v.clone(), 0)).collect();
while let Some(w) = stack.pop() {
if let Some(ps) = pred.get(&w) {
for v in ps {
if sigma[&w] != 0 {
let add = sigma[v] * (SCALE + delta[&w]) / sigma[&w];
*delta.get_mut(v).unwrap() += add;
}
}
}
if &w != s {
*betw.get_mut(&w).unwrap() += delta[&w];
}
}
}
betw.into_iter().map(|(v, b)| vec![v, Value::Int(b)]).collect()
}
fn undirected_neighbors(g: &Graph, v: &Value) -> BTreeSet<Value> {
let mut s: BTreeSet<Value> = g.neighbors(v).iter().map(|(t, _)| t.clone()).collect();
if let Some(srcs) = g.inc.get(v) {
s.extend(srcs.iter().cloned());
}
s.remove(v);
s
}
fn label_propagation(g: &Graph, iters: i64) -> Vec<Tuple> {
let idx: BTreeMap<&Value, i64> = g.nodes.iter().enumerate().map(|(i, v)| (v, i as i64)).collect();
let mut label: BTreeMap<Value, i64> = g.nodes.iter().map(|v| (v.clone(), idx[v])).collect();
for _ in 0..iters.max(0) {
for v in &g.nodes {
let mut tally: BTreeMap<i64, i64> = BTreeMap::new();
for n in undirected_neighbors(g, v) {
*tally.entry(label[&n]).or_default() += 1;
}
if let Some((&best, _)) = tally.iter().max_by(|a, b| a.1.cmp(b.1).then(b.0.cmp(a.0))) {
label.insert(v.clone(), best);
}
}
}
label.into_iter().map(|(v, l)| vec![v, Value::Int(l)]).collect()
}
fn clustering(g: &Graph) -> Vec<Tuple> {
let connected = |a: &Value, b: &Value| {
g.neighbors(a).iter().any(|(t, _)| t == b) || g.neighbors(b).iter().any(|(t, _)| t == a)
};
g.nodes
.iter()
.map(|v| {
let neigh: Vec<Value> = undirected_neighbors(g, v).into_iter().collect();
let deg = neigh.len() as i64;
let coeff = if deg < 2 {
0
} else {
let mut links = 0i64;
for i in 0..neigh.len() {
for j in (i + 1)..neigh.len() {
if connected(&neigh[i], &neigh[j]) {
links += 1;
}
}
}
SCALE * 2 * links / (deg * (deg - 1))
};
vec![v.clone(), Value::Int(coeff)]
})
.collect()
}
fn louvain(g: &Graph) -> Vec<Tuple> {
let idx: BTreeMap<&Value, i64> = g.nodes.iter().enumerate().map(|(i, v)| (v, i as i64)).collect();
let mut comm: BTreeMap<Value, i64> = g.nodes.iter().map(|v| (v.clone(), idx[v])).collect();
for _ in 0..g.nodes.len().max(1) {
let mut moved = false;
for v in &g.nodes {
let mut tally: BTreeMap<i64, i64> = BTreeMap::new();
for n in undirected_neighbors(g, v) {
*tally.entry(comm[&n]).or_default() += 1;
}
if let Some((&best, _)) = tally.iter().max_by(|a, b| a.1.cmp(b.1).then(b.0.cmp(a.0))) {
if comm[v] != best {
comm.insert(v.clone(), best);
moved = true;
}
}
}
if !moved {
break;
}
}
comm.into_iter().map(|(v, c)| vec![v, Value::Int(c)]).collect()
}
fn kruskal(g: &Graph) -> Vec<Tuple> {
let mut edges: BTreeMap<(Value, Value), i64> = BTreeMap::new();
for (f, tos) in &g.out {
for (t, w) in tos {
let key = if f <= t { (f.clone(), t.clone()) } else { (t.clone(), f.clone()) };
let e = edges.entry(key).or_insert(*w);
if *w < *e {
*e = *w;
}
}
}
let mut sorted: Vec<((Value, Value), i64)> = edges.into_iter().collect();
sorted.sort_by(|a, b| a.1.cmp(&b.1).then(a.0.cmp(&b.0)));
let idx: BTreeMap<&Value, usize> = g.nodes.iter().enumerate().map(|(i, v)| (v, i)).collect();
let mut parent: Vec<usize> = (0..g.nodes.len()).collect();
fn find(p: &mut Vec<usize>, x: usize) -> usize {
let mut r = x;
while p[r] != r {
r = p[r];
}
let mut c = x;
while p[c] != r {
let n = p[c];
p[c] = r;
c = n;
}
r
}
let mut out = Vec::new();
for ((f, t), w) in sorted {
let (a, b) = (find(&mut parent, idx[&f]), find(&mut parent, idx[&t]));
if a != b {
parent[a] = b;
out.push(vec![f, t, Value::Int(w)]);
}
}
out
}
fn dijkstra(
g: &Graph,
a: &Value,
b: &Value,
blocked_edges: &BTreeSet<(Value, Value)>,
blocked_nodes: &BTreeSet<Value>,
) -> Option<(Vec<Value>, i64)> {
let mut dist: BTreeMap<Value, i64> = BTreeMap::new();
let mut prev: BTreeMap<Value, Value> = BTreeMap::new();
let mut done: BTreeSet<Value> = BTreeSet::new();
dist.insert(a.clone(), 0);
loop {
let next = dist
.iter()
.filter(|(n, _)| !done.contains(*n))
.min_by_key(|(n, d)| (**d, (*n).clone()))
.map(|(n, d)| (n.clone(), *d));
let (u, du) = next?;
done.insert(u.clone());
if &u == b {
break;
}
for (v, w) in g.neighbors(&u) {
if blocked_nodes.contains(v) || blocked_edges.contains(&(u.clone(), v.clone())) {
continue;
}
let nd = du + w;
if dist.get(v).map_or(true, |&old| nd < old) {
dist.insert(v.clone(), nd);
prev.insert(v.clone(), u.clone());
}
}
}
let cost = *dist.get(b)?;
match reconstruct(&prev, a, b) {
Value::List(path) => Some((path, cost)),
_ => None,
}
}
fn yen(g: &Graph, a: &Value, b: &Value, k: usize) -> Vec<Tuple> {
let mut accepted: Vec<(Vec<Value>, i64)> = Vec::new();
let first = match dijkstra(g, a, b, &BTreeSet::new(), &BTreeSet::new()) {
Some(x) => x,
None => return vec![],
};
accepted.push(first);
let mut candidates: BTreeSet<(i64, Vec<Value>)> = BTreeSet::new();
while accepted.len() < k {
let prev = accepted.last().unwrap().0.clone();
for i in 0..prev.len().saturating_sub(1) {
let spur = prev[i].clone();
let root = &prev[..=i];
let mut blocked_edges = BTreeSet::new();
for (p, _) in &accepted {
if p.len() > i && &p[..=i] == root {
blocked_edges.insert((p[i].clone(), p[i + 1].clone()));
}
}
let blocked_nodes: BTreeSet<Value> = root[..i].iter().cloned().collect();
if let Some((spur_path, _)) = dijkstra(g, &spur, b, &blocked_edges, &blocked_nodes) {
let mut total = root[..i].to_vec();
total.extend(spur_path);
let cost = path_cost(g, &total);
if let Some(c) = cost {
candidates.insert((c, total));
}
}
}
match candidates.iter().next().cloned() {
Some((c, path)) => {
candidates.remove(&(c, path.clone()));
if !accepted.iter().any(|(p, _)| p == &path) {
accepted.push((path, c));
}
}
None => break,
}
}
accepted.into_iter().map(|(p, c)| vec![Value::List(p), Value::Int(c)]).collect()
}
fn path_cost(g: &Graph, path: &[Value]) -> Option<i64> {
let mut total = 0i64;
for w in path.windows(2) {
let edge = g.neighbors(&w[0]).iter().find(|(t, _)| *t == w[1]).map(|(_, c)| *c)?;
total += edge;
}
Some(total)
}
fn random_walk(g: &Graph, start: &Value, steps: i64, times: i64, seed: u64) -> Vec<Tuple> {
let mut visits: BTreeMap<Value, i64> = BTreeMap::new();
let mut state = seed.wrapping_add(0x9E3779B97F4A7C15);
let mut next = || {
state = state.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
state >> 33
};
for _ in 0..times.max(0) {
let mut cur = start.clone();
for _ in 0..steps.max(0) {
let neigh = g.neighbors(&cur);
if neigh.is_empty() {
break;
}
let idx = (next() as usize) % neigh.len();
cur = neigh[idx].0.clone();
*visits.entry(cur.clone()).or_default() += 1;
}
}
visits.into_iter().map(|(v, c)| vec![v, Value::Int(c)]).collect()
}