use std::hash::Hash;
use std::ops::Add;
use std::ops::Div;
use std::ops::Mul;
use std::ops::Neg;
use std::ops::Sub;
use petgraph::graph::{DiGraph, EdgeReference};
use num::traits::Inv;
use num::traits::One;
use num::traits::Zero;
use crate::neg_cycle::NegCycleFinder;
pub trait ParametricAPI<E, R>
where
R: Copy + PartialOrd,
E: Clone,
{
fn distance(&self, ratio: &R, edge: &EdgeReference<R>) -> R;
fn zero_cancel(&self, cycle: &[EdgeReference<R>]) -> R;
}
#[derive(Debug)]
pub struct MaxParametricSolver<'a, V, R, P>
where
R: Copy
+ PartialOrd
+ Add<Output = R>
+ Sub<Output = R>
+ Mul<Output = R>
+ Div<Output = R>
+ Neg<Output = R>
+ Inv<Output = R>,
V: Eq + Hash + Clone,
P: ParametricAPI<V, R>,
{
ncf: NegCycleFinder<'a, V, R>,
omega: P,
}
impl<'a, V, R, P> MaxParametricSolver<'a, V, R, P>
where
R: Copy
+ PartialOrd
+ Zero
+ One
+ Add<Output = R>
+ Sub<Output = R>
+ Mul<Output = R>
+ Div<Output = R>
+ Neg<Output = R>
+ Inv<Output = R>,
V: Eq + Hash + Clone,
P: ParametricAPI<V, R>,
{
pub fn new(gra: &'a DiGraph<V, R>, omega: P) -> Self {
Self {
ncf: NegCycleFinder::new(gra),
omega,
}
}
pub fn run(&mut self, dist: &mut [R], ratio: &mut R) -> Vec<EdgeReference<'a, R>> {
let mut r_min = *ratio;
let mut c_min = Vec::<EdgeReference<R>>::new();
let mut cycle = Vec::<EdgeReference<R>>::new();
loop {
if let Some(ci) = self.ncf.howard(dist, |e| self.omega.distance(ratio, &e)) {
let ri = self.omega.zero_cancel(&ci);
if r_min > ri {
r_min = ri;
c_min = ci;
}
}
if r_min >= *ratio {
break;
}
cycle.clone_from(&c_min);
*ratio = r_min;
}
cycle
}
}
#[cfg(test)]
mod tests {
use super::*;
use petgraph::graph::DiGraph;
use num::rational::Ratio;
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 mut sum_a = Ratio::new(0, 1);
let mut sum_b = Ratio::new(0, 1);
for edge in cycle {
sum_a += *edge.weight();
sum_b += Ratio::new(1, 1);
}
sum_a / sum_b
}
}
#[test]
fn test_max_parametric_solver_simple_cycle() {
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 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);
assert!(!cycle.is_empty());
assert_eq!(ratio, Ratio::new(-1, 3));
}
#[test]
fn test_max_parametric_solver_no_cycle() {
let digraph = DiGraph::<(), Ratio<i32>>::from_edges([
(0, 1, Ratio::new(1, 1)),
(1, 2, Ratio::new(1, 1)),
(0, 2, Ratio::new(3, 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);
assert!(cycle.is_empty());
assert_eq!(ratio, Ratio::new(0, 1)); }
#[test]
fn test_max_parametric_solver_multiple_cycles() {
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(-4, 1)), (0, 2, Ratio::new(1, 1)),
]);
let mut solver = MaxParametricSolver::new(&digraph, TestParametricAPI);
let mut dist = [
Ratio::new(0, 1),
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);
assert!(!cycle.is_empty());
assert_eq!(ratio, Ratio::new(-3, 2)); }
}