1use core::fmt;
23use num_traits::Float;
24
25#[cfg(feature = "std")]
26use std::ops::{Add, Mul};
27
28#[cfg(not(feature = "std"))]
29use core::ops::{Add, Mul};
30
31use crate::TropicalError;
32
33#[derive(Debug, Clone, Copy, PartialEq)]
43pub struct TropicalNumber<T: Float> {
44 value: T,
45}
46
47impl<T: Float> TropicalNumber<T> {
48 #[inline]
58 pub fn new(value: T) -> Self {
59 Self { value }
60 }
61
62 #[inline]
64 pub fn value(&self) -> T {
65 self.value
66 }
67
68 #[inline]
72 pub fn zero() -> Self {
73 Self {
74 value: T::neg_infinity(),
75 }
76 }
77
78 #[inline]
82 pub fn neg_infinity() -> Self {
83 Self::zero()
84 }
85
86 #[inline]
88 pub fn tropical_zero() -> Self {
89 Self::zero()
90 }
91
92 #[inline]
96 pub fn one() -> Self {
97 Self { value: T::zero() }
98 }
99
100 #[inline]
102 pub fn tropical_one() -> Self {
103 Self::one()
104 }
105
106 #[inline]
108 pub fn is_zero(&self) -> bool {
109 self.value == T::neg_infinity()
110 }
111
112 #[inline]
114 pub fn is_one(&self) -> bool {
115 self.value == T::zero()
116 }
117
118 #[inline]
122 pub fn is_infinity(&self) -> bool {
123 self.is_zero()
124 }
125
126 #[inline]
128 pub fn tropical_add(&self, other: &Self) -> Self {
129 Self {
130 value: self.value.max(other.value),
131 }
132 }
133
134 #[inline]
136 pub fn tropical_mul(&self, other: &Self) -> Self {
137 Self {
138 value: self.value + other.value,
139 }
140 }
141
142 #[inline]
144 pub fn tropical_pow(&self, k: T) -> Self {
145 Self {
146 value: self.value * k,
147 }
148 }
149}
150
151impl<T: Float + fmt::Display> fmt::Display for TropicalNumber<T> {
152 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153 write!(f, "Tropical({})", self.value)
154 }
155}
156
157impl<T: Float> Add for TropicalNumber<T> {
159 type Output = Self;
160
161 #[inline]
163 fn add(self, other: Self) -> Self {
164 self.tropical_add(&other)
165 }
166}
167
168impl<T: Float> Mul for TropicalNumber<T> {
169 type Output = Self;
170
171 #[inline]
173 fn mul(self, other: Self) -> Self {
174 self.tropical_mul(&other)
175 }
176}
177
178#[derive(Debug, Clone, PartialEq)]
185pub struct TropicalMatrix<T: Float> {
186 pub rows: usize,
187 pub cols: usize,
188 pub data: Vec<Vec<TropicalNumber<T>>>,
189}
190
191impl<T: Float> TropicalMatrix<T> {
192 pub fn new(rows: usize, cols: usize) -> Self {
196 let data = vec![vec![TropicalNumber::zero(); cols]; rows];
197 Self { rows, cols, data }
198 }
199
200 pub fn from_vec(data: Vec<Vec<T>>) -> Result<Self, TropicalError> {
204 if data.is_empty() {
205 return Ok(Self::new(0, 0));
206 }
207
208 let rows = data.len();
209 let cols = data[0].len();
210
211 for (i, row) in data.iter().enumerate() {
213 if row.len() != cols {
214 return Err(TropicalError::DimensionMismatch(format!(
215 "Row {} has {} elements, expected {}",
216 i,
217 row.len(),
218 cols
219 )));
220 }
221 }
222
223 let tropical_data: Vec<Vec<TropicalNumber<T>>> = data
224 .into_iter()
225 .map(|row| row.into_iter().map(TropicalNumber::new).collect())
226 .collect();
227
228 Ok(Self {
229 rows,
230 cols,
231 data: tropical_data,
232 })
233 }
234
235 #[inline]
237 pub fn dims(&self) -> (usize, usize) {
238 (self.rows, self.cols)
239 }
240
241 #[inline]
243 pub fn rows(&self) -> usize {
244 self.rows
245 }
246
247 #[inline]
249 pub fn cols(&self) -> usize {
250 self.cols
251 }
252
253 pub fn get(&self, row: usize, col: usize) -> Result<TropicalNumber<T>, TropicalError> {
255 if row >= self.rows || col >= self.cols {
256 return Err(TropicalError::IndexOutOfBounds(format!(
257 "Index ({}, {}) out of bounds for {}×{} matrix",
258 row, col, self.rows, self.cols
259 )));
260 }
261 Ok(self.data[row][col])
262 }
263
264 pub fn set(
266 &mut self,
267 row: usize,
268 col: usize,
269 value: TropicalNumber<T>,
270 ) -> Result<(), TropicalError> {
271 if row >= self.rows || col >= self.cols {
272 return Err(TropicalError::IndexOutOfBounds(format!(
273 "Index ({}, {}) out of bounds for {}×{} matrix",
274 row, col, self.rows, self.cols
275 )));
276 }
277 self.data[row][col] = value;
278 Ok(())
279 }
280
281 pub fn identity(size: usize) -> Self {
285 let mut matrix = Self::new(size, size);
286 for i in 0..size {
287 matrix.data[i][i] = TropicalNumber::one();
288 }
289 matrix
290 }
291
292 pub fn from_log_probs(log_probs: &[Vec<T>]) -> Self {
300 if log_probs.is_empty() {
301 return Self::new(0, 0);
302 }
303
304 let rows = log_probs.len();
305 let cols = log_probs.first().map(|r| r.len()).unwrap_or(0);
306
307 let data: Vec<Vec<TropicalNumber<T>>> = log_probs
308 .iter()
309 .map(|row| row.iter().map(|&val| TropicalNumber::new(val)).collect())
310 .collect();
311
312 Self { rows, cols, data }
313 }
314
315 pub fn tropical_matmul(&self, other: &Self) -> Result<Self, TropicalError> {
319 if self.cols != other.rows {
320 return Err(TropicalError::DimensionMismatch(format!(
321 "Cannot multiply {}×{} matrix with {}×{} matrix",
322 self.rows, self.cols, other.rows, other.cols
323 )));
324 }
325
326 let mut result = Self::new(self.rows, other.cols);
327
328 for i in 0..self.rows {
329 for j in 0..other.cols {
330 let mut sum = TropicalNumber::zero();
331 for k in 0..self.cols {
332 let a = self.data[i][k];
333 let b = other.data[k][j];
334 sum = sum.tropical_add(&a.tropical_mul(&b));
335 }
336 result.data[i][j] = sum;
337 }
338 }
339
340 Ok(result)
341 }
342
343 pub fn data(&self) -> &Vec<Vec<TropicalNumber<T>>> {
345 &self.data
346 }
347
348 pub fn data_mut(&mut self) -> &mut Vec<Vec<TropicalNumber<T>>> {
350 &mut self.data
351 }
352}
353
354impl<T: Float + fmt::Display> fmt::Display for TropicalMatrix<T> {
355 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
356 writeln!(f, "TropicalMatrix [{}×{}]:", self.rows, self.cols)?;
357 for row in &self.data {
358 write!(f, " [")?;
359 for (j, val) in row.iter().enumerate() {
360 if j > 0 {
361 write!(f, ", ")?;
362 }
363 write!(f, "{}", val.value)?;
364 }
365 writeln!(f, "]")?;
366 }
367 Ok(())
368 }
369}
370
371#[derive(Debug, Clone, PartialEq)]
378pub struct TropicalMultivector<T: Float, const P: usize, const Q: usize, const R: usize> {
379 components: Vec<TropicalNumber<T>>,
383}
384
385impl<T: Float, const P: usize, const Q: usize, const R: usize> TropicalMultivector<T, P, Q, R> {
386 pub fn new() -> Self {
390 let dim = 1 << (P + Q + R); Self {
392 components: vec![TropicalNumber::zero(); dim],
393 }
394 }
395
396 pub fn from_components(components: Vec<T>) -> Result<Self, TropicalError> {
398 let dim = 1 << (P + Q + R);
399 if components.len() != dim {
400 return Err(TropicalError::DimensionMismatch(format!(
401 "Expected {} components for Cl({},{},{}), got {}",
402 dim,
403 P,
404 Q,
405 R,
406 components.len()
407 )));
408 }
409
410 Ok(Self {
411 components: components.into_iter().map(TropicalNumber::new).collect(),
412 })
413 }
414
415 #[inline]
417 pub fn dim(&self) -> usize {
418 self.components.len()
419 }
420
421 pub fn get(&self, index: usize) -> Result<TropicalNumber<T>, TropicalError> {
423 self.components.get(index).copied().ok_or_else(|| {
424 TropicalError::IndexOutOfBounds(format!(
425 "Index {} out of bounds for multivector with {} components",
426 index,
427 self.components.len()
428 ))
429 })
430 }
431
432 pub fn set(&mut self, index: usize, value: TropicalNumber<T>) -> Result<(), TropicalError> {
434 if index >= self.components.len() {
435 return Err(TropicalError::IndexOutOfBounds(format!(
436 "Index {} out of bounds for multivector with {} components",
437 index,
438 self.components.len()
439 )));
440 }
441 self.components[index] = value;
442 Ok(())
443 }
444
445 pub fn components(&self) -> &[TropicalNumber<T>] {
447 &self.components
448 }
449
450 pub fn tropical_add(&self, other: &Self) -> Self {
452 let components: Vec<_> = self
453 .components
454 .iter()
455 .zip(&other.components)
456 .map(|(a, b)| a.tropical_add(b))
457 .collect();
458
459 Self { components }
460 }
461
462 pub fn geometric_product(&self, other: &Self) -> Self {
467 let components: Vec<_> = self
470 .components
471 .iter()
472 .zip(&other.components)
473 .map(|(a, b)| a.tropical_mul(b))
474 .collect();
475
476 Self { components }
477 }
478
479 pub fn tropical_norm(&self) -> TropicalNumber<T> {
484 let mut max_val = TropicalNumber::zero();
485 for &comp in &self.components {
486 max_val = max_val.tropical_add(&comp);
487 }
488 max_val
489 }
490
491 #[inline]
493 pub fn scalar(&self) -> TropicalNumber<T> {
494 self.components[0]
495 }
496}
497
498impl<T: Float, const P: usize, const Q: usize, const R: usize> Default
499 for TropicalMultivector<T, P, Q, R>
500{
501 fn default() -> Self {
502 Self::new()
503 }
504}
505
506impl<T: Float + fmt::Display, const P: usize, const Q: usize, const R: usize> fmt::Display
507 for TropicalMultivector<T, P, Q, R>
508{
509 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
510 write!(f, "TropicalMultivector<{},{},{}>([", P, Q, R)?;
511 for (i, comp) in self.components.iter().enumerate() {
512 if i > 0 {
513 write!(f, ", ")?;
514 }
515 write!(f, "{}", comp.value)?;
516 }
517 write!(f, "])")
518 }
519}
520
521pub type StandardTropical = TropicalNumber<f64>;
524
525#[cfg(feature = "high-precision")]
527pub type ExtendedTropical = TropicalNumber<crate::ExtendedFloat>;
528
529#[cfg(test)]
530mod tests {
531 use super::*;
532
533 #[test]
534 fn test_tropical_number_basics() {
535 let a = TropicalNumber::new(3.0f64);
536 let b = TropicalNumber::new(5.0f64);
537
538 let sum = a.tropical_add(&b);
540 assert_eq!(sum.value(), 5.0);
541
542 let product = a.tropical_mul(&b);
544 assert_eq!(product.value(), 8.0);
545 }
546
547 #[test]
548 fn test_tropical_identities() {
549 let zero = TropicalNumber::<f64>::zero();
550 let one = TropicalNumber::<f64>::one();
551 let x = TropicalNumber::new(3.0);
552
553 assert!(zero.is_zero());
555 assert_eq!(x.tropical_add(&zero), x);
556
557 assert!(one.is_one());
559 assert_eq!(x.tropical_mul(&one), x);
560 }
561
562 #[test]
563 fn test_tropical_matrix_creation() {
564 let mat = TropicalMatrix::<f64>::new(2, 3);
565 assert_eq!(mat.dims(), (2, 3));
566 assert_eq!(mat.rows(), 2);
567 assert_eq!(mat.cols(), 3);
568 }
569
570 #[test]
571 fn test_tropical_matrix_identity() {
572 let id = TropicalMatrix::<f64>::identity(3);
573 assert_eq!(id.dims(), (3, 3));
574
575 for i in 0..3 {
577 assert!(id.get(i, i).unwrap().is_one());
578 }
579
580 assert!(id.get(0, 1).unwrap().is_zero());
582 assert!(id.get(1, 2).unwrap().is_zero());
583 }
584
585 #[test]
586 fn test_tropical_matrix_multiplication() {
587 let mut a = TropicalMatrix::<f64>::new(2, 2);
588 a.set(0, 0, TropicalNumber::new(1.0)).unwrap();
589 a.set(0, 1, TropicalNumber::new(2.0)).unwrap();
590 a.set(1, 0, TropicalNumber::new(3.0)).unwrap();
591 a.set(1, 1, TropicalNumber::new(4.0)).unwrap();
592
593 let id = TropicalMatrix::<f64>::identity(2);
594
595 let result = a.tropical_matmul(&id).unwrap();
597 assert_eq!(result.get(0, 0).unwrap().value(), 1.0);
598 assert_eq!(result.get(0, 1).unwrap().value(), 2.0);
599 assert_eq!(result.get(1, 0).unwrap().value(), 3.0);
600 assert_eq!(result.get(1, 1).unwrap().value(), 4.0);
601 }
602
603 #[test]
604 fn test_tropical_multivector_creation() {
605 let mv = TropicalMultivector::<f64, 3, 0, 0>::new();
606 assert_eq!(mv.dim(), 8); }
608
609 #[test]
610 fn test_tropical_multivector_addition() {
611 let mut a = TropicalMultivector::<f64, 2, 0, 0>::new();
612 let mut b = TropicalMultivector::<f64, 2, 0, 0>::new();
613
614 a.set(0, TropicalNumber::new(1.0)).unwrap();
615 a.set(1, TropicalNumber::new(2.0)).unwrap();
616
617 b.set(0, TropicalNumber::new(3.0)).unwrap();
618 b.set(1, TropicalNumber::new(1.5)).unwrap();
619
620 let sum = a.tropical_add(&b);
621
622 assert_eq!(sum.get(0).unwrap().value(), 3.0);
624 assert_eq!(sum.get(1).unwrap().value(), 2.0);
625 }
626}