pub trait ImpedanceFunction {
fn compute(&self, cost: f64) -> f64;
}
#[derive(Debug, Clone, Copy)]
pub struct ExponentialImpedance {
pub beta: f64,
}
impl ExponentialImpedance {
pub fn new(beta: f64) -> Self {
ExponentialImpedance { beta }
}
}
impl ImpedanceFunction for ExponentialImpedance {
fn compute(&self, cost: f64) -> f64 {
(-self.beta * cost).exp()
}
}
#[derive(Debug, Clone, Copy)]
pub struct PowerImpedance {
pub alpha: f64,
}
impl PowerImpedance {
pub fn new(alpha: f64) -> Self {
PowerImpedance { alpha }
}
}
impl ImpedanceFunction for PowerImpedance {
fn compute(&self, cost: f64) -> f64 {
if cost <= 0.0 {
return f64::MAX;
}
cost.powf(-self.alpha)
}
}
#[derive(Debug, Clone, Copy)]
pub struct CombinedImpedance {
pub alpha: f64,
pub beta: f64,
}
impl CombinedImpedance {
pub fn new(alpha: f64, beta: f64) -> Self {
CombinedImpedance { alpha, beta }
}
}
impl ImpedanceFunction for CombinedImpedance {
fn compute(&self, cost: f64) -> f64 {
if cost <= 0.0 {
return f64::MAX;
}
cost.powf(-self.alpha) * (-self.beta * cost).exp()
}
}
#[cfg(test)]
mod tests {
use super::*;
const EPS: f64 = 1e-10;
#[test]
fn exponential_zero_cost() {
let f = ExponentialImpedance::new(2.0);
assert_eq!(f.compute(0.0), 1.0);
}
#[test]
fn exponential_known_value() {
let f = ExponentialImpedance::new(2.0);
let expected = (-2.0_f64).exp();
assert!((f.compute(1.0) - expected).abs() < EPS);
}
#[test]
fn exponential_large_cost_near_zero() {
let f = ExponentialImpedance::new(1.0);
assert!(f.compute(50.0) < 1e-20);
}
#[test]
fn exponential_monotonically_decreasing() {
let f = ExponentialImpedance::new(0.5);
let v1 = f.compute(1.0);
let v2 = f.compute(2.0);
let v3 = f.compute(5.0);
assert!(v1 > v2);
assert!(v2 > v3);
}
#[test]
fn power_unit_cost() {
let f = PowerImpedance::new(2.0);
assert_eq!(f.compute(1.0), 1.0);
}
#[test]
fn power_known_value() {
let f = PowerImpedance::new(2.0);
assert_eq!(f.compute(2.0), 0.25);
}
#[test]
fn power_zero_cost_returns_max() {
let f = PowerImpedance::new(1.5);
assert_eq!(f.compute(0.0), f64::MAX);
}
#[test]
fn power_negative_cost_returns_max() {
let f = PowerImpedance::new(1.5);
assert_eq!(f.compute(-1.0), f64::MAX);
}
#[test]
fn power_monotonically_decreasing() {
let f = PowerImpedance::new(1.0);
let v1 = f.compute(1.0);
let v2 = f.compute(2.0);
let v3 = f.compute(4.0);
assert!(v1 > v2);
assert!(v2 > v3);
}
#[test]
fn combined_known_value() {
let f = CombinedImpedance::new(1.0, 1.0);
let expected = (-1.0_f64).exp();
assert!((f.compute(1.0) - expected).abs() < EPS);
}
#[test]
fn combined_another_value() {
let f = CombinedImpedance::new(1.0, 1.0);
let expected = 0.5 * (-2.0_f64).exp();
assert!((f.compute(2.0) - expected).abs() < EPS);
}
#[test]
fn combined_zero_cost_returns_max() {
let f = CombinedImpedance::new(1.0, 1.0);
assert_eq!(f.compute(0.0), f64::MAX);
}
#[test]
fn combined_monotonically_decreasing() {
let f = CombinedImpedance::new(0.5, 0.5);
let v1 = f.compute(1.0);
let v2 = f.compute(2.0);
let v3 = f.compute(5.0);
assert!(v1 > v2);
assert!(v2 > v3);
}
}