use netoptim_rs::neg_cycle::NegCycleFinder;
use netoptim_rs::parametric::{MaxParametricSolver, ParametricAPI};
use num::rational::Ratio;
use petgraph::graph::DiGraph;
use petgraph::graph::EdgeReference;
use petgraph::visit::EdgeRef as _;
fn main() {
println!("=== Negative Cycle Detection Examples ===\n");
println!("Example 1: Simple Negative Cycle");
println!("----------------------------------");
let digraph = DiGraph::<(), Ratio<i32>>::from_edges([
(0, 1, Ratio::new(1, 1)),
(1, 2, Ratio::new(1, 1)),
(2, 0, Ratio::new(-3, 1)), ]);
let mut ncf = NegCycleFinder::new(&digraph);
let mut dist = [Ratio::new(0, 1), Ratio::new(0, 1), Ratio::new(0, 1)];
match ncf.howard(&mut dist, |e| *e.weight()) {
Some(cycle) => {
println!("Negative cycle found:");
let cycle_weight: Ratio<i32> = cycle.iter().map(|e| *e.weight()).sum();
println!(
" Nodes: {:?}",
cycle.iter().map(|e| e.source().index()).collect::<Vec<_>>()
);
println!(" Cycle weight: {}", cycle_weight);
}
None => println!("No negative cycle found"),
}
println!("\n\nExample 2: Multiple Negative Cycles");
println!("------------------------------------");
let digraph = DiGraph::<(), Ratio<i32>>::from_edges([
(0, 1, Ratio::new(1, 1)),
(1, 0, Ratio::new(-2, 1)), (2, 3, Ratio::new(1, 1)),
(3, 2, Ratio::new(-3, 1)), (0, 2, Ratio::new(1, 1)),
]);
let mut ncf = NegCycleFinder::new(&digraph);
let mut dist = [
Ratio::new(0, 1),
Ratio::new(0, 1),
Ratio::new(0, 1),
Ratio::new(0, 1),
];
match ncf.howard(&mut dist, |e| *e.weight()) {
Some(cycle) => {
println!("One negative cycle found:");
let cycle_weight: Ratio<i32> = cycle.iter().map(|e| *e.weight()).sum();
println!(
" Nodes: {:?}",
cycle.iter().map(|e| e.source().index()).collect::<Vec<_>>()
);
println!(" Cycle weight: {}", cycle_weight);
}
None => println!("No negative cycle found"),
}
println!("\n\nExample 3: No Negative Cycle");
println!("-----------------------------");
let digraph = DiGraph::<(), Ratio<i32>>::from_edges([
(0, 1, Ratio::new(1, 1)),
(1, 2, Ratio::new(1, 1)),
(2, 3, Ratio::new(1, 1)),
]);
let mut ncf = NegCycleFinder::new(&digraph);
let mut dist = [
Ratio::new(0, 1),
Ratio::new(0, 1),
Ratio::new(0, 1),
Ratio::new(0, 1),
];
match ncf.howard(&mut dist, |e| *e.weight()) {
Some(cycle) => {
println!("Negative cycle found (unexpected!):");
println!(
" Nodes: {:?}",
cycle.iter().map(|e| e.source().index()).collect::<Vec<_>>()
);
}
None => println!("No negative cycle found (as expected)"),
}
println!("\n\nExample 4: Parametric Optimization");
println!("------------------------------------");
struct TestParametricAPI;
impl ParametricAPI<(), Ratio<i32>> for TestParametricAPI {
fn distance(&self, ratio: &Ratio<i32>, edge: &EdgeReference<Ratio<i32>>) -> Ratio<i32> {
*edge.weight() - *ratio
}
fn zero_cancel(&self, cycle: &[EdgeReference<Ratio<i32>>]) -> Ratio<i32> {
let sum_a: Ratio<i32> = cycle.iter().map(|e| *e.weight()).sum();
let sum_b = Ratio::new(cycle.len() as i32, 1);
sum_a / sum_b
}
}
let digraph = DiGraph::<(), Ratio<i32>>::from_edges([
(0, 1, Ratio::new(2, 1)),
(1, 2, Ratio::new(3, 1)),
(2, 0, Ratio::new(-7, 1)),
]);
let mut solver = MaxParametricSolver::new(&digraph, TestParametricAPI);
let mut dist = [Ratio::new(0, 1), Ratio::new(0, 1), Ratio::new(0, 1)];
let mut ratio = Ratio::new(0, 1);
let cycle = solver.run(&mut dist, &mut ratio);
if !cycle.is_empty() {
println!("Optimal cycle found:");
println!(" Cycle ratio: {}", ratio);
println!(" Cycle edges:");
for edge in &cycle {
println!(
" {} -> {} (weight: {})",
edge.source().index(),
edge.target().index(),
edge.weight()
);
}
} else {
println!("No cycle found");
}
println!("\n\nExample 5: Currency Arbitrage Detection");
println!("------------------------------------------");
println!("Detecting arbitrage opportunities in currency exchange rates");
let digraph = DiGraph::<(), Ratio<i32>>::from_edges([
(0, 1, Ratio::new(85, 100)), (1, 2, Ratio::new(88, 100)), (2, 0, Ratio::new(135, 100)), ]);
let mut ncf = NegCycleFinder::new(&digraph);
let mut dist = [Ratio::new(0, 1), Ratio::new(0, 1), Ratio::new(0, 1)];
match ncf.howard(&mut dist, |e| {
let w = *e.weight();
-Ratio::new(100, 1) / w }) {
Some(cycle) => {
println!("Arbitrage opportunity detected!");
println!(
" Cycle: {:?}",
cycle.iter().map(|e| e.source().index()).collect::<Vec<_>>()
);
let profit: Ratio<i32> = cycle.iter().map(|e| *e.weight()).product();
println!(" Exchange rate product: {}", profit);
if profit < Ratio::new(100, 100) {
println!(" This indicates potential arbitrage!");
}
}
None => println!("No arbitrage opportunity found"),
}
}