1use super::semiring::{Tropical, TropicalMin};
10
11#[derive(Debug, Clone)]
13pub struct TropicalMatrix {
14 rows: usize,
15 cols: usize,
16 data: Vec<f64>,
17}
18
19impl TropicalMatrix {
20 pub fn zeros(rows: usize, cols: usize) -> Self {
22 Self {
23 rows,
24 cols,
25 data: vec![f64::NEG_INFINITY; rows * cols],
26 }
27 }
28
29 pub fn identity(n: usize) -> Self {
31 let mut m = Self::zeros(n, n);
32 for i in 0..n {
33 m.set(i, i, 0.0);
34 }
35 m
36 }
37
38 pub fn from_rows(data: Vec<Vec<f64>>) -> Self {
40 let rows = data.len();
41 let cols = if rows > 0 { data[0].len() } else { 0 };
42 let flat: Vec<f64> = data.into_iter().flatten().collect();
43 Self {
44 rows,
45 cols,
46 data: flat,
47 }
48 }
49
50 #[inline]
52 pub fn get(&self, i: usize, j: usize) -> f64 {
53 if i >= self.rows || j >= self.cols {
54 return f64::NEG_INFINITY;
55 }
56 self.data[i * self.cols + j]
57 }
58
59 #[inline]
61 pub fn set(&mut self, i: usize, j: usize, val: f64) {
62 if i >= self.rows || j >= self.cols {
63 return;
64 }
65 self.data[i * self.cols + j] = val;
66 }
67
68 pub fn dims(&self) -> (usize, usize) {
70 (self.rows, self.cols)
71 }
72
73 pub fn mul(&self, other: &Self) -> Self {
75 assert_eq!(self.cols, other.rows, "Dimension mismatch");
76
77 let mut result = Self::zeros(self.rows, other.cols);
78
79 for i in 0..self.rows {
80 for k in 0..other.cols {
81 let mut max_val = f64::NEG_INFINITY;
82 for j in 0..self.cols {
83 let a = self.get(i, j);
84 let b = other.get(j, k);
85
86 if a != f64::NEG_INFINITY && b != f64::NEG_INFINITY {
87 max_val = max_val.max(a + b);
88 }
89 }
90 result.set(i, k, max_val);
91 }
92 }
93
94 result
95 }
96
97 pub fn pow(&self, n: usize) -> Self {
99 assert_eq!(self.rows, self.cols, "Must be square");
100
101 if n == 0 {
102 return Self::identity(self.rows);
103 }
104
105 let mut result = self.clone();
106 for _ in 1..n {
107 result = result.mul(self);
108 }
109 result
110 }
111
112 pub fn closure(&self) -> Self {
115 assert_eq!(self.rows, self.cols, "Must be square");
116 let n = self.rows;
117
118 let mut result = Self::identity(n);
119 let mut power = self.clone();
120
121 for _ in 0..n {
122 for i in 0..n {
124 for j in 0..n {
125 let old = result.get(i, j);
126 let new = power.get(i, j);
127 result.set(i, j, old.max(new));
128 }
129 }
130 power = power.mul(self);
131 }
132
133 result
134 }
135
136 pub fn max_cycle_mean(&self) -> f64 {
139 assert_eq!(self.rows, self.cols, "Must be square");
140 let n = self.rows;
141
142 let mut d = vec![vec![f64::NEG_INFINITY; n + 1]; n];
144
145 for i in 0..n {
147 d[i][0] = 0.0;
148 }
149
150 for k in 1..=n {
152 for i in 0..n {
153 for j in 0..n {
154 let w = self.get(i, j);
155 if w != f64::NEG_INFINITY && d[j][k - 1] != f64::NEG_INFINITY {
156 d[i][k] = d[i][k].max(w + d[j][k - 1]);
157 }
158 }
159 }
160 }
161
162 let mut lambda = f64::NEG_INFINITY;
164 for i in 0..n {
165 if d[i][n] != f64::NEG_INFINITY {
166 let mut min_ratio = f64::INFINITY;
167 for k in 0..n {
168 if k < n && d[i][k] != f64::NEG_INFINITY {
170 let divisor = (n - k) as f64;
171 if divisor > 0.0 {
172 let ratio = (d[i][n] - d[i][k]) / divisor;
173 min_ratio = min_ratio.min(ratio);
174 }
175 }
176 }
177 lambda = lambda.max(min_ratio);
178 }
179 }
180
181 lambda
182 }
183}
184
185#[derive(Debug, Clone)]
187pub struct TropicalEigen {
188 pub eigenvalue: f64,
190 pub eigenvector: Vec<f64>,
192}
193
194impl TropicalEigen {
195 pub fn power_iteration(matrix: &TropicalMatrix, max_iters: usize) -> Option<Self> {
198 let n = matrix.rows;
199 if n == 0 {
200 return None;
201 }
202
203 let mut v: Vec<f64> = vec![0.0; n];
205 let mut eigenvalue = 0.0f64;
206
207 for _ in 0..max_iters {
208 let mut av = vec![f64::NEG_INFINITY; n];
210 for i in 0..n {
211 for j in 0..n {
212 let aij = matrix.get(i, j);
213 if aij != f64::NEG_INFINITY && v[j] != f64::NEG_INFINITY {
214 av[i] = av[i].max(aij + v[j]);
215 }
216 }
217 }
218
219 let max_av = av.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
221 if max_av == f64::NEG_INFINITY {
222 return None;
223 }
224
225 let new_eigenvalue = max_av - v.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
227
228 for i in 0..n {
230 v[i] = av[i] - max_av;
231 }
232
233 if (new_eigenvalue - eigenvalue).abs() < 1e-10 {
235 return Some(TropicalEigen {
236 eigenvalue: new_eigenvalue,
237 eigenvector: v,
238 });
239 }
240
241 eigenvalue = new_eigenvalue;
242 }
243
244 Some(TropicalEigen {
245 eigenvalue,
246 eigenvector: v,
247 })
248 }
249}
250
251#[derive(Debug, Clone)]
253pub struct MinPlusMatrix {
254 rows: usize,
255 cols: usize,
256 data: Vec<f64>,
257}
258
259impl MinPlusMatrix {
260 pub fn from_adjacency(adj: Vec<Vec<f64>>) -> Self {
262 let rows = adj.len();
263 let cols = if rows > 0 { adj[0].len() } else { 0 };
264 let data: Vec<f64> = adj.into_iter().flatten().collect();
265 Self { rows, cols, data }
266 }
267
268 #[inline]
270 pub fn get(&self, i: usize, j: usize) -> f64 {
271 if i >= self.rows || j >= self.cols {
272 return f64::INFINITY;
273 }
274 self.data[i * self.cols + j]
275 }
276
277 #[inline]
279 pub fn set(&mut self, i: usize, j: usize, val: f64) {
280 if i >= self.rows || j >= self.cols {
281 return;
282 }
283 self.data[i * self.cols + j] = val;
284 }
285
286 pub fn all_pairs_shortest_paths(&self) -> Self {
288 let n = self.rows;
289 let mut dist = self.clone();
290
291 for k in 0..n {
292 for i in 0..n {
293 for j in 0..n {
294 let via_k = dist.get(i, k) + dist.get(k, j);
295 if via_k < dist.get(i, j) {
296 dist.set(i, j, via_k);
297 }
298 }
299 }
300 }
301
302 dist
303 }
304}
305
306#[cfg(test)]
307mod tests {
308 use super::*;
309
310 #[test]
311 fn test_tropical_matrix_mul() {
312 let a = TropicalMatrix::from_rows(vec![vec![0.0, 1.0], vec![f64::NEG_INFINITY, 2.0]]);
314
315 let a2 = a.mul(&a);
317
318 assert!((a2.get(0, 1) - 3.0).abs() < 1e-10); }
320
321 #[test]
322 fn test_tropical_identity() {
323 let i = TropicalMatrix::identity(3);
324 let a = TropicalMatrix::from_rows(vec![
325 vec![1.0, 2.0, 3.0],
326 vec![4.0, 5.0, 6.0],
327 vec![7.0, 8.0, 9.0],
328 ]);
329
330 let ia = i.mul(&a);
331 for row in 0..3 {
332 for col in 0..3 {
333 assert!((ia.get(row, col) - a.get(row, col)).abs() < 1e-10);
334 }
335 }
336 }
337
338 #[test]
339 fn test_max_cycle_mean() {
340 let a = TropicalMatrix::from_rows(vec![
343 vec![f64::NEG_INFINITY, 3.0],
344 vec![1.0, f64::NEG_INFINITY],
345 ]);
346
347 let mcm = a.max_cycle_mean();
348 assert!((mcm - 2.0).abs() < 1e-10);
349 }
350
351 #[test]
352 fn test_floyd_warshall() {
353 let adj = MinPlusMatrix::from_adjacency(vec![
355 vec![0.0, 1.0, 5.0],
356 vec![f64::INFINITY, 0.0, 2.0],
357 vec![f64::INFINITY, f64::INFINITY, 0.0],
358 ]);
359
360 let dist = adj.all_pairs_shortest_paths();
361
362 assert!((dist.get(0, 2) - 3.0).abs() < 1e-10);
364 }
365}