use std::fmt::Debug;
use super::*;
use crate::rings::Semiring;
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub enum TropicalElement<F: Field> {
Element(F),
NegInfinity,
}
impl<F: Field> Eq for TropicalElement<F> {}
impl<F: Field> TropicalElement<F> {
pub fn new(value: F) -> Self { TropicalElement::Element(value) }
pub fn value(&self) -> TropicalElement<F> { *self }
}
impl<F: Field + PartialOrd> Add for TropicalElement<F> {
type Output = Self;
fn add(self, other: Self) -> Self::Output {
match self {
TropicalElement::Element(a) => {
match other {
TropicalElement::Element(b) => {
Self::Element(if a > b { a } else { b })
},
TropicalElement::NegInfinity => Self::Element(a),
}
},
TropicalElement::NegInfinity => match other {
TropicalElement::Element(b) => Self::Element(b),
TropicalElement::NegInfinity => Self::NegInfinity,
},
}
}
}
impl<F: Field + PartialOrd> AddAssign for TropicalElement<F> {
fn add_assign(&mut self, rhs: Self) { *self = *self + rhs; }
}
impl<F: Field + PartialOrd> Mul for TropicalElement<F> {
type Output = Self;
fn mul(self, other: Self) -> Self::Output {
match self {
TropicalElement::Element(a) => {
match other {
TropicalElement::Element(b) => {
#[allow(clippy::suspicious_arithmetic_impl)]
Self::Element(a + b)
},
TropicalElement::NegInfinity => Self::NegInfinity,
}
},
TropicalElement::NegInfinity => Self::NegInfinity,
}
}
}
impl<F: Field + PartialOrd> MulAssign for TropicalElement<F> {
fn mul_assign(&mut self, rhs: Self) { *self = *self * rhs; }
}
impl<F: Field + PartialOrd> Zero for TropicalElement<F> {
fn zero() -> Self { TropicalElement::NegInfinity }
fn is_zero(&self) -> bool { *self == TropicalElement::NegInfinity }
}
impl<F: Field + PartialOrd> One for TropicalElement<F> {
fn one() -> Self { TropicalElement::Element(F::zero()) }
}
impl<F: Field + PartialOrd> Additive for TropicalElement<F> {}
impl<F: Field + PartialOrd> Multiplicative for TropicalElement<F> {}
impl<F: Field + PartialOrd> Semiring for TropicalElement<F> {}
#[derive(Debug, PartialEq, Eq)]
pub struct BilinearForm<const N: usize, F>
where
F: Field + PartialOrd,
[(); N * (N + 1) / 2]:, {
matrix: [TropicalElement<F>; N * (N + 1) / 2],
}
impl<const N: usize, F> BilinearForm<N, F>
where
F: Field + PartialOrd,
[(); N * (N + 1) / 2]:, {
pub fn new(matrix: [[TropicalElement<F>; N]; N]) -> Self {
let mut upper_triangular = [TropicalElement::NegInfinity; N * (N + 1) / 2];
let mut idx = 0;
for (i, row) in matrix.iter().enumerate() {
for (_j, &element) in row.iter().enumerate().skip(i) {
upper_triangular[idx] = element;
idx += 1;
}
}
Self { matrix: upper_triangular }
}
fn get(&self, i: usize, j: usize) -> TropicalElement<F> {
if i <= j {
let n = N;
let idx = i
.checked_mul(2 * n - i + 1)
.and_then(|x| x.checked_div(2))
.and_then(|x| x.checked_add(j - i))
.expect("Index calculation overflow");
self.matrix[idx]
} else {
self.get(j, i)
}
}
pub fn evaluate(
&self,
x: &[TropicalElement<F>; N],
y: &[TropicalElement<F>; N],
) -> TropicalElement<F> {
let mut result = TropicalElement::<F>::zero();
for (i, &xi) in x.iter().enumerate() {
for (j, &yj) in y.iter().enumerate() {
let term = xi * self.get(i, j) * yj;
result = match (term, result) {
(TropicalElement::Element(_), TropicalElement::NegInfinity) => term,
(TropicalElement::NegInfinity, TropicalElement::Element(_)) => result,
(TropicalElement::Element(t), TropicalElement::Element(r)) =>
if t > r {
term
} else {
result
},
(TropicalElement::NegInfinity, TropicalElement::NegInfinity) => result,
};
}
}
result
}
}
pub struct TropicalAlgebra<const N: usize, F>
where
F: Field + PartialOrd,
[(); N * (N + 1) / 2]:, {
bilinear_form: BilinearForm<N, F>,
}
impl<const N: usize, F> TropicalAlgebra<N, F>
where
F: Field + PartialOrd,
[(); N * (N + 1) / 2]:,
{
pub fn new(bilinear_form: BilinearForm<N, F>) -> Self { Self { bilinear_form } }
pub fn evaluate(
&self,
x: &[TropicalElement<F>; N],
y: &[TropicalElement<F>; N],
) -> TropicalElement<F> {
self.bilinear_form.evaluate(x, y)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tropical_element_operations() {
let a = TropicalElement::new(3.0);
let b = TropicalElement::new(5.0);
let c = TropicalElement::new(2.0);
assert_eq!(a + b, TropicalElement::new(5.0));
assert_eq!(a + c, TropicalElement::new(3.0));
assert_eq!(b + c, TropicalElement::new(5.0));
assert_eq!(a * b, TropicalElement::new(8.0));
assert_eq!(a * c, TropicalElement::new(5.0));
assert_eq!(b * c, TropicalElement::new(7.0));
assert_eq!(TropicalElement::<f64>::zero().value(), TropicalElement::NegInfinity);
assert_eq!(TropicalElement::<f64>::one().value(), TropicalElement::Element(0.0));
assert_eq!(a + TropicalElement::<f64>::zero(), a);
assert_eq!(TropicalElement::<f64>::zero() + a, a);
assert_eq!(a * TropicalElement::<f64>::one(), a);
assert_eq!(TropicalElement::<f64>::one() * a, a);
let mut x = a;
x += b;
assert_eq!(x, TropicalElement::new(5.0));
let mut y = a;
y *= b;
assert_eq!(y, TropicalElement::new(8.0));
}
#[test]
fn test_bilinear_form_evaluation() {
let matrix = [[TropicalElement::new(1.0), TropicalElement::new(2.0)], [
TropicalElement::new(2.0),
TropicalElement::new(1.0),
]];
let bilinear_form = BilinearForm::new(matrix);
let x = [TropicalElement::new(3.0), TropicalElement::new(4.0)];
let y = [TropicalElement::new(5.0), TropicalElement::new(6.0)];
assert_eq!(bilinear_form.evaluate(&x, &y), TropicalElement::new(11.0));
let zero = [TropicalElement::<f64>::zero(), TropicalElement::<f64>::zero()];
assert_eq!(bilinear_form.evaluate(&zero, &y), TropicalElement::<f64>::zero());
assert_eq!(bilinear_form.evaluate(&x, &zero), TropicalElement::<f64>::zero());
let one = [TropicalElement::<f64>::one(), TropicalElement::<f64>::one()];
assert_eq!(bilinear_form.evaluate(&one, &one), TropicalElement::new(2.0));
}
#[test]
fn test_tropical_algebra() {
let matrix = [[TropicalElement::new(1.0), TropicalElement::new(2.0)], [
TropicalElement::new(2.0),
TropicalElement::new(1.0),
]];
let bilinear_form = BilinearForm::new(matrix);
let algebra = TropicalAlgebra::new(bilinear_form);
let x = [TropicalElement::new(3.0), TropicalElement::new(4.0)];
let y = [TropicalElement::new(5.0), TropicalElement::new(6.0)];
assert_eq!(algebra.evaluate(&x, &y), TropicalElement::new(11.0));
let a = [TropicalElement::new(0.0), TropicalElement::new(1.0)];
let b = [TropicalElement::new(2.0), TropicalElement::new(3.0)];
assert_eq!(algebra.evaluate(&a, &b), TropicalElement::new(5.0));
}
#[test]
fn test_tropical_element_ordering() {
let a = TropicalElement::new(3.0);
let b = TropicalElement::new(5.0);
let c = TropicalElement::new(3.0);
assert!(a < b);
assert!(b > a);
assert!(a <= c);
assert!(a >= c);
assert_eq!(a, c);
}
#[test]
fn test_tropical_element_zero_one_properties() {
let a = TropicalElement::new(3.0);
let zero = TropicalElement::<f64>::zero();
let one = TropicalElement::<f64>::one();
assert!(zero.is_zero());
dbg!(a);
dbg!(zero);
assert_eq!(a + zero, a);
assert_eq!(zero + a, a);
assert_eq!(a * zero, zero);
assert_eq!(zero * a, zero);
assert_eq!(a * one, a);
assert_eq!(one * a, a);
}
}