1use std::fmt::Debug;
53
54use super::*;
55use crate::rings::Semiring;
56
57#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
65pub enum TropicalElement<F: Field> {
66 Element(F),
68 NegInfinity,
70}
71
72impl<F: Field> Eq for TropicalElement<F> {}
73impl<F: Field> TropicalElement<F> {
74 pub fn new(value: F) -> Self { TropicalElement::Element(value) }
76
77 pub fn value(&self) -> TropicalElement<F> { *self }
79}
80
81impl<F: Field + PartialOrd> Add for TropicalElement<F> {
82 type Output = Self;
83
84 fn add(self, other: Self) -> Self::Output {
85 match self {
86 TropicalElement::Element(a) => {
87 match other {
88 TropicalElement::Element(b) => {
89 Self::Element(if a > b { a } else { b })
91 },
92 TropicalElement::NegInfinity => Self::Element(a),
93 }
94 },
95 TropicalElement::NegInfinity => match other {
96 TropicalElement::Element(b) => Self::Element(b),
97 TropicalElement::NegInfinity => Self::NegInfinity,
98 },
99 }
100 }
101}
102
103impl<F: Field + PartialOrd> AddAssign for TropicalElement<F> {
104 fn add_assign(&mut self, rhs: Self) { *self = *self + rhs; }
105}
106
107impl<F: Field + PartialOrd> Mul for TropicalElement<F> {
108 type Output = Self;
109
110 fn mul(self, other: Self) -> Self::Output {
111 match self {
112 TropicalElement::Element(a) => {
113 match other {
114 TropicalElement::Element(b) => {
115 #[allow(clippy::suspicious_arithmetic_impl)]
117 Self::Element(a + b)
118 },
119 TropicalElement::NegInfinity => Self::NegInfinity,
120 }
121 },
122 TropicalElement::NegInfinity => Self::NegInfinity,
123 }
124 }
125}
126
127impl<F: Field + PartialOrd> MulAssign for TropicalElement<F> {
128 fn mul_assign(&mut self, rhs: Self) { *self = *self * rhs; }
129}
130
131impl<F: Field + PartialOrd> Zero for TropicalElement<F> {
132 fn zero() -> Self { TropicalElement::NegInfinity }
133
134 fn is_zero(&self) -> bool { *self == TropicalElement::NegInfinity }
135}
136
137impl<F: Field + PartialOrd> One for TropicalElement<F> {
138 fn one() -> Self { TropicalElement::Element(F::zero()) }
139}
140
141impl<F: Field + PartialOrd> Additive for TropicalElement<F> {}
142impl<F: Field + PartialOrd> Multiplicative for TropicalElement<F> {}
143impl<F: Field + PartialOrd> Semiring for TropicalElement<F> {}
144
145#[derive(Debug, PartialEq, Eq)]
147pub struct BilinearForm<const N: usize, F>
148where
149 F: Field + PartialOrd,
150 [(); N * (N + 1) / 2]:, {
151 matrix: [TropicalElement<F>; N * (N + 1) / 2],
154}
155
156impl<const N: usize, F> BilinearForm<N, F>
157where
158 F: Field + PartialOrd,
159 [(); N * (N + 1) / 2]:, {
161 pub fn new(matrix: [[TropicalElement<F>; N]; N]) -> Self {
164 let mut upper_triangular = [TropicalElement::NegInfinity; N * (N + 1) / 2];
165 let mut idx = 0;
166 for (i, row) in matrix.iter().enumerate() {
167 for (_j, &element) in row.iter().enumerate().skip(i) {
168 upper_triangular[idx] = element;
169 idx += 1;
170 }
171 }
172 Self { matrix: upper_triangular }
173 }
174
175 fn get(&self, i: usize, j: usize) -> TropicalElement<F> {
178 if i <= j {
179 let n = N;
183 let idx = i
184 .checked_mul(2 * n - i + 1)
185 .and_then(|x| x.checked_div(2))
186 .and_then(|x| x.checked_add(j - i))
187 .expect("Index calculation overflow");
188 self.matrix[idx]
189 } else {
190 self.get(j, i)
192 }
193 }
194
195 pub fn evaluate(
197 &self,
198 x: &[TropicalElement<F>; N],
199 y: &[TropicalElement<F>; N],
200 ) -> TropicalElement<F> {
201 let mut result = TropicalElement::<F>::zero();
202
203 for (i, &xi) in x.iter().enumerate() {
204 for (j, &yj) in y.iter().enumerate() {
205 let term = xi * self.get(i, j) * yj;
206 result = match (term, result) {
210 (TropicalElement::Element(_), TropicalElement::NegInfinity) => term,
211 (TropicalElement::NegInfinity, TropicalElement::Element(_)) => result,
212 (TropicalElement::Element(t), TropicalElement::Element(r)) =>
213 if t > r {
214 term
215 } else {
216 result
217 },
218 (TropicalElement::NegInfinity, TropicalElement::NegInfinity) => result,
219 };
220 }
221 }
222
223 result
224 }
225}
226
227pub struct TropicalAlgebra<const N: usize, F>
229where
230 F: Field + PartialOrd,
231 [(); N * (N + 1) / 2]:, {
232 bilinear_form: BilinearForm<N, F>,
234}
235
236impl<const N: usize, F> TropicalAlgebra<N, F>
237where
238 F: Field + PartialOrd,
239 [(); N * (N + 1) / 2]:,
240{
241 pub fn new(bilinear_form: BilinearForm<N, F>) -> Self { Self { bilinear_form } }
243
244 pub fn evaluate(
246 &self,
247 x: &[TropicalElement<F>; N],
248 y: &[TropicalElement<F>; N],
249 ) -> TropicalElement<F> {
250 self.bilinear_form.evaluate(x, y)
251 }
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257
258 #[test]
259 fn test_tropical_element_operations() {
260 let a = TropicalElement::new(3.0);
261 let b = TropicalElement::new(5.0);
262 let c = TropicalElement::new(2.0);
263
264 assert_eq!(a + b, TropicalElement::new(5.0));
266 assert_eq!(a + c, TropicalElement::new(3.0));
267 assert_eq!(b + c, TropicalElement::new(5.0));
268
269 assert_eq!(a * b, TropicalElement::new(8.0));
271 assert_eq!(a * c, TropicalElement::new(5.0));
272 assert_eq!(b * c, TropicalElement::new(7.0));
273
274 assert_eq!(TropicalElement::<f64>::zero().value(), TropicalElement::NegInfinity);
276 assert_eq!(TropicalElement::<f64>::one().value(), TropicalElement::Element(0.0));
277
278 assert_eq!(a + TropicalElement::<f64>::zero(), a);
280 assert_eq!(TropicalElement::<f64>::zero() + a, a);
281
282 assert_eq!(a * TropicalElement::<f64>::one(), a);
284 assert_eq!(TropicalElement::<f64>::one() * a, a);
285
286 let mut x = a;
288 x += b;
289 assert_eq!(x, TropicalElement::new(5.0));
290
291 let mut y = a;
293 y *= b;
294 assert_eq!(y, TropicalElement::new(8.0));
295 }
296
297 #[test]
298 fn test_bilinear_form_evaluation() {
299 let matrix = [[TropicalElement::new(1.0), TropicalElement::new(2.0)], [
300 TropicalElement::new(2.0),
301 TropicalElement::new(1.0),
302 ]];
303 let bilinear_form = BilinearForm::new(matrix);
304
305 let x = [TropicalElement::new(3.0), TropicalElement::new(4.0)];
307 let y = [TropicalElement::new(5.0), TropicalElement::new(6.0)];
308
309 assert_eq!(bilinear_form.evaluate(&x, &y), TropicalElement::new(11.0));
314
315 let zero = [TropicalElement::<f64>::zero(), TropicalElement::<f64>::zero()];
317 assert_eq!(bilinear_form.evaluate(&zero, &y), TropicalElement::<f64>::zero());
318 assert_eq!(bilinear_form.evaluate(&x, &zero), TropicalElement::<f64>::zero());
319
320 let one = [TropicalElement::<f64>::one(), TropicalElement::<f64>::one()];
322 assert_eq!(bilinear_form.evaluate(&one, &one), TropicalElement::new(2.0));
323 }
324
325 #[test]
326 fn test_tropical_algebra() {
327 let matrix = [[TropicalElement::new(1.0), TropicalElement::new(2.0)], [
328 TropicalElement::new(2.0),
329 TropicalElement::new(1.0),
330 ]];
331 let bilinear_form = BilinearForm::new(matrix);
332 let algebra = TropicalAlgebra::new(bilinear_form);
333
334 let x = [TropicalElement::new(3.0), TropicalElement::new(4.0)];
336 let y = [TropicalElement::new(5.0), TropicalElement::new(6.0)];
337 assert_eq!(algebra.evaluate(&x, &y), TropicalElement::new(11.0));
338
339 let a = [TropicalElement::new(0.0), TropicalElement::new(1.0)];
341 let b = [TropicalElement::new(2.0), TropicalElement::new(3.0)];
342 assert_eq!(algebra.evaluate(&a, &b), TropicalElement::new(5.0));
343 }
344
345 #[test]
346 fn test_tropical_element_ordering() {
347 let a = TropicalElement::new(3.0);
348 let b = TropicalElement::new(5.0);
349 let c = TropicalElement::new(3.0);
350
351 assert!(a < b);
352 assert!(b > a);
353 assert!(a <= c);
354 assert!(a >= c);
355 assert_eq!(a, c);
356 }
357
358 #[test]
359 fn test_tropical_element_zero_one_properties() {
360 let a = TropicalElement::new(3.0);
361 let zero = TropicalElement::<f64>::zero();
362 let one = TropicalElement::<f64>::one();
363
364 assert!(zero.is_zero());
366 dbg!(a);
367 dbg!(zero);
368 assert_eq!(a + zero, a);
369 assert_eq!(zero + a, a);
370 assert_eq!(a * zero, zero);
371 assert_eq!(zero * a, zero);
372
373 assert_eq!(a * one, a);
375 assert_eq!(one * a, a);
376 }
377}