use crate::parametric::{MaxParametricSolver, ParametricAPI};
use num::traits::Inv;
use petgraph::graph::{DiGraph, EdgeReference};
use std::marker::PhantomData;
use std::ops::{Add, Div, Mul, Neg, Sub};
struct CycleRatioAPI<V, D, F1, F2> {
get_cost: F1,
get_time: F2,
_phantom: PhantomData<(V, D)>,
}
impl<V, D, F1, F2> ParametricAPI<V, D> for CycleRatioAPI<V, D, F1, F2>
where
V: Clone,
D: Copy + PartialOrd + Add<Output = D> + Sub<Output = D> + Mul<Output = D> + Div<Output = D>,
F1: Fn(&EdgeReference<D>) -> D,
F2: Fn(&EdgeReference<D>) -> D,
{
fn distance(&self, ratio: &D, edge: &EdgeReference<D>) -> D {
(self.get_cost)(edge) - *ratio * (self.get_time)(edge)
}
fn zero_cancel(&self, cycle: &[EdgeReference<D>]) -> D {
let mut total_cost = (self.get_cost)(&cycle[0]);
let mut total_time = (self.get_time)(&cycle[0]);
for edge in &cycle[1..] {
total_cost = total_cost + (self.get_cost)(edge);
total_time = total_time + (self.get_time)(edge);
}
total_cost / total_time
}
}
pub fn min_cycle_ratio<'a, V, D, F1, F2>(
gra: &'a DiGraph<V, D>,
r0: &mut D,
get_cost: F1,
get_time: F2,
dist: &mut [D],
) -> Vec<EdgeReference<'a, D>>
where
D: Copy
+ PartialOrd
+ Add<Output = D>
+ Sub<Output = D>
+ Mul<Output = D>
+ Div<Output = D>
+ Neg<Output = D>
+ Inv<Output = D>
+ num::traits::Zero
+ num::traits::One,
V: Eq + std::hash::Hash + Clone,
F1: Fn(&EdgeReference<D>) -> D,
F2: Fn(&EdgeReference<D>) -> D,
{
let api = CycleRatioAPI {
get_cost,
get_time,
_phantom: PhantomData,
};
let mut solver = MaxParametricSolver::new(gra, api);
solver.run(dist, r0)
}
#[cfg(test)]
mod tests {
use super::*;
use num::rational::Ratio;
#[test]
fn test_min_cycle_ratio_simple() {
let digraph = DiGraph::<(), Ratio<i32>>::from_edges([
(0, 1, Ratio::new(5, 1)),
(1, 2, Ratio::new(1, 1)),
(2, 3, Ratio::new(1, 1)),
(3, 4, Ratio::new(1, 1)),
(4, 0, Ratio::new(1, 1)),
]);
let mut r0 = Ratio::new(5, 1);
let mut dist = [Ratio::new(0, 1); 5];
let get_cost = |edge: &EdgeReference<Ratio<i32>>| *edge.weight();
let get_time = |_edge: &EdgeReference<Ratio<i32>>| Ratio::new(1, 1);
let cycle = min_cycle_ratio(&digraph, &mut r0, get_cost, get_time, &mut dist);
assert!(!cycle.is_empty());
assert_eq!(cycle.len(), 5);
}
}