use graphops::{AdjacencyMatrix, Graph, WeightedGraph};
use structops::soft_shortest_path::{soft_shortest_path_edge_marginals, Edge};
fn build_network() -> Vec<Vec<f64>> {
let n = 6;
let mut adj = vec![vec![0.0; n]; n];
adj[0][1] = 2.0; adj[0][2] = 4.0;
adj[1][3] = 5.0; adj[1][4] = 1.0;
adj[2][3] = 6.0; adj[2][4] = 3.0;
adj[3][5] = 2.0;
adj[4][5] = 1.0;
adj
}
fn extract_edges(adj: &[Vec<f64>]) -> Vec<Edge> {
let graph = AdjacencyMatrix(adj);
let n = graph.node_count();
let mut edges = Vec::new();
for u in 0..n {
let neighbors = graph.neighbors(u);
for v in neighbors {
if u < v {
let cost = graph.edge_weight(u, v);
edges.push(Edge {
from: u,
to: v,
cost,
});
}
}
}
edges
}
fn edge_label(e: &Edge) -> String {
format!("{}->{} (cost={:.1})", e.from, e.to, e.cost)
}
fn main() {
let adj = build_network();
let n = adj.len();
let edges = extract_edges(&adj);
println!("Graph: {n} nodes, {} edges", edges.len());
println!();
for (i, e) in edges.iter().enumerate() {
println!(" edge {i}: {}", edge_label(e));
}
println!();
println!("Feasible paths (source=0, sink=5):");
println!(" 0->1->3->5 cost = 9");
println!(" 0->1->4->5 cost = 4 <-- shortest");
println!(" 0->2->3->5 cost = 12");
println!(" 0->2->4->5 cost = 8");
println!();
let gammas = [0.01, 0.1, 0.5, 1.0, 2.0, 5.0, 50.0];
print!("{:>22}", "edge \\ gamma");
for &g in &gammas {
print!(" {:>7}", format!("{g}"));
}
println!();
print!("{:>22}", "");
for _ in &gammas {
print!(" -------");
}
println!();
let results: Vec<(f64, Vec<f64>)> = gammas
.iter()
.map(|&g| {
soft_shortest_path_edge_marginals(n, &edges, g).expect("computation should succeed")
})
.collect();
for (i, e) in edges.iter().enumerate() {
print!("{:>22}", edge_label(e));
for (value, marginals) in &results {
let _ = value; print!(" {:>7.4}", marginals[i]);
}
println!();
}
println!();
print!("{:>22}", "soft value");
for (value, _) in &results {
print!(" {:>7.2}", value);
}
println!();
println!();
println!("Interpretation:");
println!(" gamma -> 0: marginals concentrate on the shortest path (0->1->4->5, cost=4).");
println!(" Edges 0->1, 1->4, 4->5 each get marginal ~1.0; all others ~0.0.");
println!(" gamma -> inf: marginals spread across all paths. Every edge that appears in");
println!(" at least one s-t path gets positive mass, approaching the uniform");
println!(" distribution over paths.");
println!();
let (_, p_cold) = &results[0]; let shortest_path_edges: Vec<usize> = edges
.iter()
.enumerate()
.filter(|(_, e)| {
(e.from == 0 && e.to == 1) || (e.from == 1 && e.to == 4) || (e.from == 4 && e.to == 5)
})
.map(|(i, _)| i)
.collect();
let off_path: f64 = p_cold
.iter()
.enumerate()
.filter(|(i, _)| !shortest_path_edges.contains(i))
.map(|(_, &p)| p)
.sum();
println!("Sanity check at gamma=0.01: off-shortest-path mass = {off_path:.6} (should be ~0).");
}