Repository navigation
Expand file tree
/
Copy pathTropical_Matrices_Full.thy
More file actions
2441 lines (2264 loc) · 105 KB
/
Copy pathTropical_Matrices_Full.thy
File metadata and controls
2441 lines (2264 loc) · 105 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
(* SPDX-License-Identifier: MPL-2.0 *)
theory Tropical_Matrices_Full
imports Tropical_v2
begin
text \<open>
Tropical matrix theory for both max-plus and min-plus semirings.
This theory develops:
\<^item> Matrix type synonyms @{text trop_mat} and @{text tropm_mat}.
\<^item> Matrix operations: identity, pointwise addition, tropical multiplication,
power, and the close operator @{text "I \<oplus> A"}.
\<^item> Walk combinatorics: sets @{text "walks n k i j"} and @{text "walks_le n k i j"},
finiteness, and factorisation lemmas.
\<^item> @{text path_weight}: tropical product of edge weights along a walk.
\<^item> KEY THEOREM @{text trop_mat_pow_eq_sum_walks}: matrix power equals tropical
sum over the set of walks of the right length.
\<^item> Closure theorems and the Floyd–Warshall / Bellman–Ford correctness results.
Verified against Isabelle 2025-1.
\<close>
(* ================================================================== *)
section \<open>Part I Matrix Types and Operations\<close>
(* ================================================================== *)
(* ------------------------------------------------------------------ *)
subsection \<open>1 Matrix Type Synonyms\<close>
(* ------------------------------------------------------------------ *)
text \<open>
We represent @{text n}-by-@{text n} matrices as functions from indices
to tropical values. Index bounds (@{text "i < n"}, @{text "j < n"})
are enforced by preconditions on every matrix operation.
@{text trop_mat} — max-plus matrix (for longest-path problems)
@{text tropm_mat} — min-plus matrix (for shortest-path / WCET)
\<close>
type_synonym trop_mat = "nat \<Rightarrow> nat \<Rightarrow> tropical"
type_synonym tropm_mat = "nat \<Rightarrow> nat \<Rightarrow> tropical_min"
(* ------------------------------------------------------------------ *)
subsection \<open>2 Identity Matrices\<close>
(* ------------------------------------------------------------------ *)
text \<open>
@{text "trop_mat_id n i j"} is the tropical multiplicative identity matrix:
@{text "Fin 0"} on the diagonal, @{text NegInf} elsewhere.
Similarly @{text tropm_mat_id} uses @{text "Fin' 0"} and @{text PosInf}.
\<close>
definition trop_mat_id :: "nat \<Rightarrow> trop_mat" where
"trop_mat_id n i j \<equiv> if i = j then Fin 0 else NegInf"
definition tropm_mat_id :: "nat \<Rightarrow> tropm_mat" where
"tropm_mat_id n i j \<equiv> if i = j then Fin' 0 else PosInf"
lemma trop_mat_id_diag [simp]:
"trop_mat_id n i i = Fin 0"
by (simp add: trop_mat_id_def)
lemma trop_mat_id_off_diag [simp]:
"i \<noteq> j \<Longrightarrow> trop_mat_id n i j = NegInf"
by (simp add: trop_mat_id_def)
lemma tropm_mat_id_diag [simp]:
"tropm_mat_id n i i = Fin' 0"
by (simp add: tropm_mat_id_def)
lemma tropm_mat_id_off_diag [simp]:
"i \<noteq> j \<Longrightarrow> tropm_mat_id n i j = PosInf"
by (simp add: tropm_mat_id_def)
(* ------------------------------------------------------------------ *)
subsection \<open>3 Pointwise Addition (Join)\<close>
(* ------------------------------------------------------------------ *)
text \<open>
Pointwise tropical addition: entry-wise @{text max} for max-plus,
@{text min} for min-plus. Also written @{text "\<oplus>"} in the prose.
\<close>
definition trop_mat_add :: "nat \<Rightarrow> trop_mat \<Rightarrow> trop_mat \<Rightarrow> trop_mat" where
"trop_mat_add n A B i j \<equiv> A i j + B i j"
definition tropm_mat_add :: "nat \<Rightarrow> tropm_mat \<Rightarrow> tropm_mat \<Rightarrow> tropm_mat" where
"tropm_mat_add n A B i j \<equiv> A i j + B i j"
(* ------------------------------------------------------------------ *)
subsection \<open>4 Tropical Matrix Multiplication\<close>
(* ------------------------------------------------------------------ *)
text \<open>
@{text "trop_mat_mul n A B i j = \<Sum>_{k < n} A i k * B k j"}
The sum is the tropical (= max-plus) sum, which equals @{term max}
over the finite set @{text "{..<n}"}. Similarly for min-plus.
Note: for @{typ tropical}, @{term "(\<Sum>)"} is the @{class comm_monoid_add}
fold, so it implements @{text trop_add} with identity @{text NegInf}.
\<close>
definition trop_mat_mul :: "nat \<Rightarrow> trop_mat \<Rightarrow> trop_mat \<Rightarrow> trop_mat" where
"trop_mat_mul n A B i j \<equiv> \<Sum> k \<in> {..<n}. A i k * B k j"
definition tropm_mat_mul :: "nat \<Rightarrow> tropm_mat \<Rightarrow> tropm_mat \<Rightarrow> tropm_mat" where
"tropm_mat_mul n A B i j \<equiv> \<Sum> k \<in> {..<n}. A i k * B k j"
(* ------------------------------------------------------------------ *)
subsection \<open>5 Matrix Close: I \<oplus> A\<close>
(* ------------------------------------------------------------------ *)
text \<open>
The close of @{text A} is @{text "I \<oplus> A"} — entrywise max/min with the
identity matrix. Adding the identity ensures there is always a weight
for the zero-hop path (the empty walk staying at a node).
\<close>
definition trop_mat_close :: "nat \<Rightarrow> trop_mat \<Rightarrow> trop_mat" where
"trop_mat_close n A i j \<equiv> A i j + trop_mat_id n i j"
definition tropm_mat_close :: "nat \<Rightarrow> tropm_mat \<Rightarrow> tropm_mat" where
"tropm_mat_close n A i j \<equiv> A i j + tropm_mat_id n i j"
lemma trop_mat_close_diag [simp]:
"trop_mat_close n A i i = A i i + Fin 0"
by (simp add: trop_mat_close_def)
lemma trop_mat_close_off_diag [simp]:
"i \<noteq> j \<Longrightarrow> trop_mat_close n A i j = A i j + NegInf"
by (simp add: trop_mat_close_def trop_mat_id_def)
(* ------------------------------------------------------------------ *)
subsection \<open>6 Matrix Power\<close>
(* ------------------------------------------------------------------ *)
text \<open>
Right-iterated matrix multiplication:
@{text "A^0 = I"}, @{text "A^{Suc k} = A^k \<cdot> A"}.
Right multiplication is used because the walk-induction in
@{text trop_mat_pow_eq_sum_walks} appends one edge at a time on the right.
\<close>
fun trop_mat_pow :: "nat \<Rightarrow> trop_mat \<Rightarrow> nat \<Rightarrow> trop_mat" where
"trop_mat_pow n A 0 = trop_mat_id n"
| "trop_mat_pow n A (Suc k) = trop_mat_mul n (trop_mat_pow n A k) A"
fun tropm_mat_pow :: "nat \<Rightarrow> tropm_mat \<Rightarrow> nat \<Rightarrow> tropm_mat" where
"tropm_mat_pow n A 0 = tropm_mat_id n"
| "tropm_mat_pow n A (Suc k) = tropm_mat_mul n (tropm_mat_pow n A k) A"
(* ================================================================== *)
section \<open>Part II Basic Matrix Algebraic Laws\<close>
(* ================================================================== *)
(* ------------------------------------------------------------------ *)
subsection \<open>7 Auxiliary: Tropical Sum over Unit Interval\<close>
(* ------------------------------------------------------------------ *)
text \<open>
Singleton tropical sums simplify matrix identity laws.
\<close>
lemma trop_sum_singleton_lt:
"(\<Sum> k \<in> {..<1 :: nat}. f k) = f 0"
by simp
(* ------------------------------------------------------------------ *)
subsection \<open>8 Identity Law for Matrix Multiplication\<close>
(* ------------------------------------------------------------------ *)
lemma trop_mat_mul_id_right:
"i < n \<Longrightarrow> j < n \<Longrightarrow>
trop_mat_mul n A (trop_mat_id n) i j = A i j"
proof -
assume hi: "i < n" and hj: "j < n"
have "trop_mat_mul n A (trop_mat_id n) i j
= (\<Sum> k \<in> {..<n}. A i k * trop_mat_id n k j)"
by (simp add: trop_mat_mul_def)
also have "\<dots> = (\<Sum> k \<in> {..<n}. if k = j then A i k * Fin 0 else A i k * NegInf)"
by (rule sum.cong) (simp_all add: trop_mat_id_def)
also have "\<dots> = (\<Sum> k \<in> {..<n}. if k = j then A i k * Fin 0 else (0 :: tropical))"
by (rule sum.cong) (simp_all add: zero_tropical_def[symmetric])
also have "\<dots> = A i j * Fin 0"
using hj by (simp add: sum.delta[OF finite_lessThan])
also have "\<dots> = A i j"
by (simp add: times_tropical_def)
finally show ?thesis .
qed
lemma trop_mat_mul_id_left:
"i < n \<Longrightarrow> j < n \<Longrightarrow>
trop_mat_mul n (trop_mat_id n) A i j = A i j"
proof -
assume hi: "i < n" and hj: "j < n"
have "trop_mat_mul n (trop_mat_id n) A i j
= (\<Sum> k \<in> {..<n}. trop_mat_id n i k * A k j)"
by (simp add: trop_mat_mul_def)
also have "\<dots> = (\<Sum> k \<in> {..<n}. if k = i then Fin 0 * A k j else NegInf * A k j)"
by (rule sum.cong) (simp_all add: trop_mat_id_def)
also have "\<dots> = (\<Sum> k \<in> {..<n}. if k = i then Fin 0 * A k j else (0 :: tropical))"
by (rule sum.cong) (simp_all add: zero_tropical_def[symmetric])
also have "\<dots> = Fin 0 * A i j"
using hi by (simp add: sum.delta[OF finite_lessThan])
also have "\<dots> = A i j"
by (simp add: times_tropical_def)
finally show ?thesis .
qed
(* ------------------------------------------------------------------ *)
subsection \<open>9 Associativity of Matrix Multiplication\<close>
(* ------------------------------------------------------------------ *)
text \<open>
@{text trop_mat_mul_assoc}: proved by unfolding and swapping the order of
summation, then applying distributivity of tropical multiplication over
tropical addition. The key step is
@{text "(\<Sum>_k. (\<Sum>_l. f k l)) = (\<Sum>_l. (\<Sum>_k. f k l))"}
which holds because @{typ tropical} is a @{class comm_monoid_add}.
\<close>
lemma trop_mat_mul_assoc:
"i < n \<Longrightarrow> j < n \<Longrightarrow>
trop_mat_mul n (trop_mat_mul n A B) C i j =
trop_mat_mul n A (trop_mat_mul n B C) i j"
proof -
assume hi: "i < n" and hj: "j < n"
have "trop_mat_mul n (trop_mat_mul n A B) C i j
= (\<Sum> l \<in> {..<n}. (\<Sum> k \<in> {..<n}. A i k * B k l) * C l j)"
by (simp add: trop_mat_mul_def)
also have "\<dots> = (\<Sum> l \<in> {..<n}. \<Sum> k \<in> {..<n}. A i k * B k l * C l j)"
by (rule sum.cong, simp)
(simp add: sum_distrib_right)
also have "\<dots> = (\<Sum> k \<in> {..<n}. \<Sum> l \<in> {..<n}. A i k * B k l * C l j)"
by (rule sum.swap)
also have "\<dots> = (\<Sum> k \<in> {..<n}. A i k * (\<Sum> l \<in> {..<n}. B k l * C l j))"
by (rule sum.cong, simp)
(simp add: sum_distrib_left mult.assoc)
also have "\<dots> = trop_mat_mul n A (trop_mat_mul n B C) i j"
by (simp add: trop_mat_mul_def)
finally show ?thesis .
qed
(* ------------------------------------------------------------------ *)
subsection \<open>10 Power Laws\<close>
(* ------------------------------------------------------------------ *)
lemma trop_mat_pow_zero [simp]:
"trop_mat_pow n A 0 = trop_mat_id n"
by simp
lemma trop_mat_pow_one:
"i < n \<Longrightarrow> j < n \<Longrightarrow> trop_mat_pow n A 1 i j = A i j"
by (simp add: trop_mat_mul_id_left)
lemma trop_mat_pow_Suc_right:
"trop_mat_pow n A (Suc k) = trop_mat_mul n (trop_mat_pow n A k) A"
by simp
lemma trop_mat_pow_add:
"i < n \<Longrightarrow> j < n \<Longrightarrow>
trop_mat_pow n A (p + q) i j = trop_mat_mul n (trop_mat_pow n A p) (trop_mat_pow n A q) i j"
proof (induction q arbitrary: i j)
case 0
then show ?case
by (simp add: trop_mat_mul_id_right)
next
case (Suc q)
have "trop_mat_pow n A (p + Suc q) i j
= trop_mat_mul n (trop_mat_pow n A (p + q)) A i j"
by simp
also have "\<dots> = trop_mat_mul n (trop_mat_mul n (trop_mat_pow n A p) (trop_mat_pow n A q)) A i j"
(is "?L = ?R")
proof -
have step: "trop_mat_pow n A (p + q) i k =
trop_mat_mul n (trop_mat_pow n A p) (trop_mat_pow n A q) i k"
if "k < n" for k
using Suc.IH[OF Suc.prems(1) that] .
have "?L = (\<Sum>k\<in>{..<n}. trop_mat_pow n A (p + q) i k * A k j)"
by (simp add: trop_mat_mul_def)
also have "\<dots> = (\<Sum>k\<in>{..<n}. trop_mat_mul n (trop_mat_pow n A p)
(trop_mat_pow n A q) i k * A k j)"
by (rule sum.cong[OF refl]) (simp add: step)
also have "\<dots> = ?R"
by (simp add: trop_mat_mul_def)
finally show ?thesis .
qed
also have "\<dots> = trop_mat_mul n (trop_mat_pow n A p) (trop_mat_mul n (trop_mat_pow n A q) A) i j"
using Suc.prems by (simp add: trop_mat_mul_assoc)
also have "\<dots> = trop_mat_mul n (trop_mat_pow n A p) (trop_mat_pow n A (Suc q)) i j"
by simp
finally show ?case .
qed
(* ================================================================== *)
section \<open>Part III Walk Combinatorics\<close>
(* ================================================================== *)
(* ------------------------------------------------------------------ *)
subsection \<open>11 Walk Sets\<close>
(* ------------------------------------------------------------------ *)
text \<open>
A @{text k}-edge walk in an @{text n}-vertex graph from @{text i} to @{text j}
is a list of vertices with @{text "Suc k"} elements (including both endpoints)
such that:
\<^item> the first element is @{text i},
\<^item> the last element is @{text j},
\<^item> every element is a valid vertex index, i.e.\ in @{text "{..<n}"}.
Note: a 0-edge walk is a singleton list @{text "[i]"} with @{text "i = j"}.
\<close>
definition walks :: "nat \<Rightarrow> nat \<Rightarrow> nat \<Rightarrow> nat \<Rightarrow> nat list set" where
"walks n k i j \<equiv>
{ w . length w = Suc k \<and> hd w = i \<and> last w = j \<and> set w \<subseteq> {..<n} }"
text \<open>
@{text "walks_le n k i j"} collects all walks with @{text "\<le> k"} edges.
\<close>
definition walks_le :: "nat \<Rightarrow> nat \<Rightarrow> nat \<Rightarrow> nat \<Rightarrow> nat list set" where
"walks_le n k i j \<equiv> \<Union> m \<in> {..k}. walks n m i j"
(* ------------------------------------------------------------------ *)
subsection \<open>12 Elementary Walk Facts\<close>
(* ------------------------------------------------------------------ *)
lemma walks_0:
"i < n \<Longrightarrow>
walks n 0 i i = {[i]}"
unfolding walks_def
by (auto simp: length_Suc_conv hd_conv_nth last_conv_nth)
lemma walks_0_empty_if_neq:
"i \<noteq> j \<Longrightarrow> walks n 0 i j = {}"
unfolding walks_def
by (auto simp: length_Suc_conv)
lemma walks_le_0:
"i < n \<Longrightarrow> walks_le n 0 i i = {[i]}"
unfolding walks_le_def
by (simp add: walks_0)
lemma walk_nonempty:
"w \<in> walks n k i j \<Longrightarrow> w \<noteq> []"
unfolding walks_def
by auto
lemma walk_hd:
"w \<in> walks n k i j \<Longrightarrow> hd w = i"
unfolding walks_def by simp
lemma walk_last:
"w \<in> walks n k i j \<Longrightarrow> last w = j"
unfolding walks_def by simp
lemma walk_vertices_bounded:
"w \<in> walks n k i j \<Longrightarrow> set w \<subseteq> {..<n}"
unfolding walks_def by simp
lemma walks_bounds:
"w \<in> walks n k i j \<Longrightarrow> i < n \<and> j < n"
proof -
assume h: "w \<in> walks n k i j"
have ne: "w \<noteq> []" using walk_nonempty[OF h] .
have si: "i \<in> set w" by (metis walk_hd[OF h] hd_in_set ne)
have sj: "j \<in> set w" by (metis walk_last[OF h] last_in_set ne)
show ?thesis using walk_vertices_bounded[OF h] si sj by auto
qed
(* ------------------------------------------------------------------ *)
subsection \<open>13 Finiteness of Walk Sets\<close>
(* ------------------------------------------------------------------ *)
lemma finite_walks:
"finite (walks n k i j)"
proof -
have sub: "walks n k i j \<subseteq> {w . length w = Suc k \<and> set w \<subseteq> {..<n}}"
unfolding walks_def by blast
have set_eq: "{w :: nat list . length w = Suc k \<and> set w \<subseteq> {..<n}}
= {w :: nat list . set w \<subseteq> {..<n} \<and> length w = Suc k}"
by blast
have "finite ({..<n} :: nat set)" by simp
hence "finite {w :: nat list . set w \<subseteq> {..<n} \<and> length w = Suc k}"
by (rule finite_lists_length_eq)
with set_eq have "finite {w :: nat list . length w = Suc k \<and> set w \<subseteq> {..<n}}"
by simp
thus ?thesis using sub by (rule finite_subset[rotated])
qed
lemma finite_walks_le:
"finite (walks_le n k i j)"
unfolding walks_le_def
by (rule finite_UN_I) (simp_all add: finite_walks)
(* ------------------------------------------------------------------ *)
subsection \<open>14 Walk Factorisation\<close>
(* ------------------------------------------------------------------ *)
text \<open>
A @{text "Suc k"}-edge walk can be uniquely decomposed as a @{text k}-edge
walk (to some intermediate vertex @{text m}) followed by one more edge
(to the destination @{text j}). This is the combinatorial core of the
matrix-power induction.
\<close>
lemma walks_Suc:
"walks n (Suc k) i j =
(\<Union> m \<in> {..<n}. (\<lambda>w. w @ [j]) ` walks n k i m \<inter>
{w . last (butlast w) = m \<and> j < n})"
unfolding walks_def
proof (rule set_eqI)
fix w :: "nat list"
show "w \<in> {w. length w = Suc (Suc k) \<and> hd w = i \<and> last w = j \<and> set w \<subseteq> {..<n}} \<longleftrightarrow>
w \<in> (\<Union> m \<in> {..<n}. (\<lambda>w. w @ [j]) ` {w. length w = Suc k \<and> hd w = i \<and> last w = m \<and> set w \<subseteq> {..<n}}
\<inter> {w. last (butlast w) = m \<and> j < n})"
proof
assume hw: "w \<in> {w. length w = Suc (Suc k) \<and> hd w = i \<and> last w = j \<and> set w \<subseteq> {..<n}}"
then have hlen: "length w = Suc (Suc k)" and hhd: "hd w = i"
and hlast: "last w = j" and hset: "set w \<subseteq> {..<n}" by simp_all
have hne: "w \<noteq> []" using hlen by auto
have hbne: "butlast w \<noteq> []"
proof
assume "butlast w = []"
hence "length (butlast w) = 0" by simp
with length_butlast[of w] hlen show False by simp
qed
let ?m = "last (butlast w)"
have hw_eq: "w = butlast w @ [j]"
by (metis append_butlast_last_id hlast hne)
have hbl_len: "length (butlast w) = Suc k"
using hlen by simp
have hbl_hd: "hd (butlast w) = i"
using hlen hhd by (cases w; cases "tl w") auto
have hbl_set: "set (butlast w) \<subseteq> {..<n}"
using hset by (auto dest: in_set_butlastD)
have hm_lt: "?m < n"
by (metis last_in_set hbne hbl_set lessThan_iff subsetD)
have hj_lt: "j < n"
by (metis last_in_set hne hset lessThan_iff hlast subsetD)
have hbl_last: "last (butlast w) = ?m" by simp
have hbl_mem: "butlast w \<in> {w. length w = Suc k \<and> hd w = i \<and> last w = ?m \<and> set w \<subseteq> {..<n}}"
using hbl_len hbl_hd hbl_set by simp
show "w \<in> (\<Union> m \<in> {..<n}. (\<lambda>w. w @ [j]) ` {w. length w = Suc k \<and> hd w = i \<and> last w = m \<and> set w \<subseteq> {..<n}}
\<inter> {w. last (butlast w) = m \<and> j < n})"
using hm_lt hbl_mem hw_eq hj_lt by blast
next
assume hw: "w \<in> (\<Union> m \<in> {..<n}. (\<lambda>w. w @ [j]) ` {w. length w = Suc k \<and> hd w = i \<and> last w = m \<and> set w \<subseteq> {..<n}}
\<inter> {w. last (butlast w) = m \<and> j < n})"
from hw obtain m where hm: "m \<in> {..<n}"
and hwm: "w \<in> (\<lambda>w. w @ [j]) ` {w. length w = Suc k \<and> hd w = i \<and> last w = m \<and> set w \<subseteq> {..<n}}
\<inter> {w. last (butlast w) = m \<and> j < n}"
by blast
from hwm have hwI: "w \<in> (\<lambda>w. w @ [j]) ` {w. length w = Suc k \<and> hd w = i \<and> last w = m \<and> set w \<subseteq> {..<n}}"
and hj: "j < n"
by auto
from hwI obtain v where hv: "v \<in> {w. length w = Suc k \<and> hd w = i \<and> last w = m \<and> set w \<subseteq> {..<n}}"
and hw_eq: "w = v @ [j]"
by (auto simp: image_iff)
from hv have hv_len: "length v = Suc k" and hv_hd: "hd v = i"
and hv_set: "set v \<subseteq> {..<n}" by simp_all
show "w \<in> {w. length w = Suc (Suc k) \<and> hd w = i \<and> last w = j \<and> set w \<subseteq> {..<n}}"
using hv_len hv_hd hv_set hj hw_eq
by (cases v) (auto simp: hd_append)
qed
qed
text \<open>
A cleaner statement: walks of length @{text "Suc k"} biject with pairs
(walk of length k, final vertex).
\<close>
lemma walks_Suc_factored:
"walks n (Suc k) i j =
{ w @ [j] | w m . m < n \<and> w \<in> walks n k i m \<and> j < n }"
unfolding walks_def
proof (rule set_eqI)
fix w :: "nat list"
show "w \<in> {w. length w = Suc (Suc k) \<and> hd w = i \<and> last w = j \<and> set w \<subseteq> {..<n}} \<longleftrightarrow>
w \<in> {w @ [j] | w m. m < n \<and> w \<in> {w. length w = Suc k \<and> hd w = i \<and> last w = m \<and> set w \<subseteq> {..<n}} \<and> j < n}"
proof
assume hw: "w \<in> {w. length w = Suc (Suc k) \<and> hd w = i \<and> last w = j \<and> set w \<subseteq> {..<n}}"
then have hlen: "length w = Suc (Suc k)" and hhd: "hd w = i"
and hlast: "last w = j" and hset: "set w \<subseteq> {..<n}" by simp_all
have hne: "w \<noteq> []" using hlen by auto
have hbne: "butlast w \<noteq> []"
proof
assume "butlast w = []"
hence "length (butlast w) = 0" by simp
with length_butlast[of w] hlen show False by simp
qed
let ?m = "last (butlast w)"
have hw_eq: "w = butlast w @ [j]"
by (metis append_butlast_last_id hlast hne)
have hbl_len: "length (butlast w) = Suc k" using hlen by simp
have hbl_hd: "hd (butlast w) = i"
using hlen hhd by (cases w; cases "tl w") auto
have hbl_set: "set (butlast w) \<subseteq> {..<n}"
using hset by (auto dest: in_set_butlastD)
have hm_lt: "?m < n"
by (metis last_in_set hbne hbl_set lessThan_iff subsetD)
have hj_lt: "j < n"
by (metis last_in_set hne hset lessThan_iff hlast subsetD)
show "w \<in> {w @ [j] | w m. m < n \<and> w \<in> {w. length w = Suc k \<and> hd w = i \<and> last w = m \<and> set w \<subseteq> {..<n}} \<and> j < n}"
using hm_lt hj_lt hw_eq hbl_len hbl_hd hbl_set
by auto
next
assume hw: "w \<in> {w @ [j] | w m. m < n \<and> w \<in> {w. length w = Suc k \<and> hd w = i \<and> last w = m \<and> set w \<subseteq> {..<n}} \<and> j < n}"
then obtain v m where hm: "m < n"
and hv: "v \<in> {w. length w = Suc k \<and> hd w = i \<and> last w = m \<and> set w \<subseteq> {..<n}}"
and hw_eq: "w = v @ [j]" and hj: "j < n"
by auto
from hv have hv_len: "length v = Suc k" and hv_hd: "hd v = i"
and hv_set: "set v \<subseteq> {..<n}" by simp_all
show "w \<in> {w. length w = Suc (Suc k) \<and> hd w = i \<and> last w = j \<and> set w \<subseteq> {..<n}}"
proof -
have hv_ne: "v \<noteq> []" using hv_len by auto
show ?thesis using hv_len hv_hd hv_set hj hw_eq hv_ne
by (auto simp: hd_append)
qed
qed
qed
(* ================================================================== *)
section \<open>Part IV Path Weight\<close>
(* ================================================================== *)
(* ------------------------------------------------------------------ *)
subsection \<open>15 Definition\<close>
(* ------------------------------------------------------------------ *)
text \<open>
The weight of a walk @{text w} in a weighted graph @{text A} is the
tropical product of the edge weights along the walk.
Base cases:
\<^item> @{text "[]"}: empty walk, weight = @{text 1} (multiplicative identity).
\<^item> @{text "[v]"}: zero-edge walk, weight = @{text 1}.
Recursive case:
\<^item> @{text "u # v # rest"}: @{text "A u v * path_weight A (v # rest)"}.
This definition is for the max-plus matrix; @{text path_weightm} is the
dual for min-plus.
\<close>
fun path_weight :: "trop_mat \<Rightarrow> nat list \<Rightarrow> tropical" where
"path_weight A [] = 1"
| "path_weight A [v] = 1"
| "path_weight A (u # v # xs) = A u v * path_weight A (v # xs)"
fun path_weightm :: "tropm_mat \<Rightarrow> nat list \<Rightarrow> tropical_min" where
"path_weightm A [] = 1"
| "path_weightm A [v] = 1"
| "path_weightm A (u # v # xs) = A u v * path_weightm A (v # xs)"
(* ------------------------------------------------------------------ *)
subsection \<open>16 Path Weight Lemmas\<close>
(* ------------------------------------------------------------------ *)
lemma path_weight_singleton [simp]:
"path_weight A [v] = 1"
by simp
lemma path_weight_cons [simp]:
"path_weight A (u # v # xs) = A u v * path_weight A (v # xs)"
by simp
text \<open>
Appending two walks: the weight of their concatenation is the product
of the individual weights.
\<close>
lemma path_weight_append:
"\<lbrakk> w1 \<noteq> []; w2 \<noteq> [] \<rbrakk> \<Longrightarrow>
path_weight A (w1 @ w2) = path_weight A (w1 @ [hd w2]) * path_weight A w2"
proof (induction w1)
case Nil then show ?case by simp
next
case (Cons u rest)
show ?case
proof (cases rest)
case Nil
have "path_weight A (u # w2) = A u (hd w2) * path_weight A w2"
using Cons.prems(2) by (cases w2) auto
then show ?thesis using Nil by simp
next
case (Cons v t)
have "path_weight A ((u # v # t) @ w2)
= A u v * path_weight A ((v # t) @ w2)"
by simp
also have "\<dots> = A u v * (path_weight A ((v # t) @ [hd w2]) * path_weight A w2)"
using Cons.IH Cons.prems(2) local.Cons by simp
also have "\<dots> = path_weight A ((u # v # t) @ [hd w2]) * path_weight A w2"
by (simp add: mult.assoc local.Cons)
finally show ?thesis
using local.Cons \<open>rest = v # t\<close> by simp
qed
qed
lemma path_weight_snoc:
"w \<noteq> [] \<Longrightarrow> path_weight A (w @ [v]) = path_weight A w * A (last w) v"
proof (induction w)
case Nil then show ?case by simp
next
case (Cons u rest)
show ?case
proof (cases rest)
case Nil
then show ?thesis by simp
next
case (Cons v' t)
have "path_weight A ((u # v' # t) @ [v])
= A u v' * path_weight A ((v' # t) @ [v])"
by simp
also have "\<dots> = A u v' * (path_weight A (v' # t) * A (last (v' # t)) v)"
using Cons.IH local.Cons by simp
also have "\<dots> = path_weight A (u # v' # t) * A (last (u # v' # t)) v"
by (simp add: mult.assoc)
finally show ?thesis by (simp add: local.Cons)
qed
qed
(* ------------------------------------------------------------------ *)
subsection \<open>16b Min-Plus and Monotonicity Auxiliaries\<close>
(* ------------------------------------------------------------------ *)
text \<open>
@{text path_weightm_snoc}: min-plus dual of @{text path_weight_snoc}.
Needed for @{text tropm_mat_pow_eq_sum_walks}.
\<close>
lemma path_weightm_append:
"\<lbrakk> w1 \<noteq> []; w2 \<noteq> [] \<rbrakk> \<Longrightarrow>
path_weightm A (w1 @ w2) = path_weightm A (w1 @ [hd w2]) * path_weightm A w2"
proof (induction w1)
case Nil then show ?case by simp
next
case (Cons u rest)
show ?case
proof (cases rest)
case Nil
have "path_weightm A (u # w2) = A u (hd w2) * path_weightm A w2"
using Cons.prems(2) by (cases w2) auto
then show ?thesis using Nil by simp
next
case (Cons v t)
have "path_weightm A ((u # v # t) @ w2)
= A u v * path_weightm A ((v # t) @ w2)"
by simp
also have "\<dots> = A u v * (path_weightm A ((v # t) @ [hd w2]) * path_weightm A w2)"
using Cons.IH Cons.prems(2) local.Cons by simp
also have "\<dots> = path_weightm A ((u # v # t) @ [hd w2]) * path_weightm A w2"
by (simp add: mult.assoc local.Cons)
finally show ?thesis
using local.Cons \<open>rest = v # t\<close> by simp
qed
qed
lemma path_weightm_snoc:
"w \<noteq> [] \<Longrightarrow> path_weightm A (w @ [v]) = path_weightm A w * A (last w) v"
proof (induction w)
case Nil then show ?case by simp
next
case (Cons u rest)
show ?case
proof (cases rest)
case Nil
then show ?thesis by simp
next
case (Cons v' t)
have "path_weightm A ((u # v' # t) @ [v])
= A u v' * path_weightm A ((v' # t) @ [v])"
by simp
also have "\<dots> = A u v' * (path_weightm A (v' # t) * A (last (v' # t)) v)"
using Cons.IH local.Cons by simp
also have "\<dots> = path_weightm A (u # v' # t) * A (last (u # v' # t)) v"
by (simp add: mult.assoc)
finally show ?thesis by (simp add: local.Cons)
qed
qed
text \<open>
Tropical multiplication is monotone in the right argument: @{text "a \<le> b \<Longrightarrow> c * a \<le> c * b"}.
Proved from @{text trop_add_le_iff} and left distributivity.
\<close>
lemma trop_mul_le_mul_right:
assumes "(a :: tropical) \<le> b"
shows "c * a \<le> c * b"
proof -
have step: "c * a + c * b = c * b"
proof -
have "c * a + c * b = c * (a + b)"
by (simp add: distrib_left)
also have "\<dots> = c * b"
using assms by (simp add: trop_add_le_iff)
finally show ?thesis .
qed
thus ?thesis by (simp add: trop_add_le_iff)
qed
text \<open>
Tropical sum of path weights over a walk set. Defined here (rather than in
Part V) because the dominance / monotonicity lemmas below depend on it.
Originally placed near the @{text trop_mat_pow_eq_sum_walks} key theorem;
refactored upward to fix dependency-order build failure.
\<close>
definition trop_walks_sum :: "trop_mat \<Rightarrow> nat list set \<Rightarrow> tropical" where
"trop_walks_sum A W \<equiv> \<Sum> w \<in> W. path_weight A w"
definition tropm_walks_sum :: "tropm_mat \<Rightarrow> nat list set \<Rightarrow> tropical_min" where
"tropm_walks_sum A W \<equiv> \<Sum> w \<in> W. path_weightm A w"
text \<open>
@{text trop_walks_sum_ge_member}: every member of a finite walk set dominates
the tropical sum. (In max-plus, the sum = max, so every member is @{text "\<le>"} the sum.)
\<close>
lemma trop_walks_sum_ge_member:
assumes "w' \<in> T" "finite T"
shows "path_weight A w' \<le> trop_walks_sum A T"
proof -
have decomp: "trop_walks_sum A T =
path_weight A w' + trop_walks_sum A (T - {w'})"
unfolding trop_walks_sum_def
using assms by (subst sum.remove) auto
have "path_weight A w' \<le> path_weight A w' + trop_walks_sum A (T - {w'})"
proof -
have "path_weight A w' + (path_weight A w' + trop_walks_sum A (T - {w'}))
= path_weight A w' + trop_walks_sum A (T - {w'})"
by (simp add: add.assoc[symmetric] tropical_add_idem)
thus ?thesis by (simp add: trop_add_le_iff)
qed
thus ?thesis using decomp by simp
qed
text \<open>
@{text trop_walks_sum_mono_subset}: the tropical walk-sum is monotone in the
walk set (larger set = higher max).
\<close>
lemma trop_walks_sum_mono_subset:
assumes "finite T" "S \<subseteq> T"
shows "trop_walks_sum A S \<le> trop_walks_sum A T"
proof -
text \<open>Non-inductive proof: decompose T = S \<union> (T-S), then use idempotency.\<close>
have decomp: "trop_walks_sum A T =
trop_walks_sum A S + trop_walks_sum A (T - S)"
unfolding trop_walks_sum_def
by (subst sum.subset_diff[OF assms(2) assms(1)]) (simp add: add.commute)
have "trop_walks_sum A S + trop_walks_sum A T
= trop_walks_sum A S + (trop_walks_sum A S + trop_walks_sum A (T - S))"
using decomp by simp
also have "\<dots> = (trop_walks_sum A S + trop_walks_sum A S) + trop_walks_sum A (T - S)"
by (simp add: add.assoc)
also have "\<dots> = trop_walks_sum A S + trop_walks_sum A (T - S)"
by (simp add: tropical_add_idem)
also have "\<dots> = trop_walks_sum A T"
using decomp by simp
finally have "trop_walks_sum A S + trop_walks_sum A T = trop_walks_sum A T" .
thus ?thesis by (simp add: trop_add_le_iff)
qed
text \<open>
@{text trop_walks_sum_dominated}: if every walk in @{text S} is dominated (in weight)
by some walk in @{text T}, then the sum over @{text S} is @{text "\<le>"} the sum over @{text T}.
\<close>
text \<open>
Helper: in a tropical (max-plus) semiring, the sum is bounded by @{text M}
iff every summand is bounded by @{text M}. We need only the easier direction.
\<close>
lemma trop_sum_le_bound:
fixes f :: "'a \<Rightarrow> tropical"
assumes "finite S" and "\<forall> w \<in> S. f w \<le> M"
shows "(\<Sum> w \<in> S. f w) \<le> M"
using assms
proof (induction S rule: finite_induct)
case empty
show ?case by (simp add: zero_tropical_def NegInf_le)
next
case (insert w S')
have hbound_w: "f w \<le> M" using insert.prems by simp
have hbound_S': "(\<Sum> v \<in> S'. f v) \<le> M"
using insert.IH insert.prems by simp
have "(\<Sum> v \<in> insert w S'. f v) = f w + (\<Sum> v \<in> S'. f v)"
using insert.hyps by simp
also have "\<dots> \<le> M"
proof -
have "(f w + (\<Sum> v \<in> S'. f v)) + M = f w + ((\<Sum> v \<in> S'. f v) + M)"
by (simp add: add.assoc)
also have "\<dots> = f w + M" using hbound_S' by (simp add: trop_add_le_iff)
also have "\<dots> = M" using hbound_w by (simp add: trop_add_le_iff)
finally have "(f w + (\<Sum> v \<in> S'. f v)) + M = M" .
thus ?thesis by (simp add: trop_add_le_iff)
qed
finally show ?case .
qed
lemma trop_walks_sum_dominated:
assumes "finite S" "finite T"
assumes dominated: "\<forall> w \<in> S. \<exists> w' \<in> T. path_weight A w \<le> path_weight A w'"
shows "trop_walks_sum A S \<le> trop_walks_sum A T"
proof -
text \<open>Each \<open>w \<in> S\<close> has its weight bounded by \<open>trop_walks_sum A T\<close>.\<close>
have bound: "\<forall> w \<in> S. path_weight A w \<le> trop_walks_sum A T"
proof (intro ballI)
fix w assume hw: "w \<in> S"
obtain w' where hw': "w' \<in> T" "path_weight A w \<le> path_weight A w'"
using dominated hw by blast
have step: "path_weight A w' \<le> trop_walks_sum A T"
using trop_walks_sum_ge_member[OF hw'(1) assms(2)] .
show "path_weight A w \<le> trop_walks_sum A T"
using hw'(2) step by (rule order_trans)
qed
show ?thesis
using trop_sum_le_bound[OF assms(1) bound]
by (simp add: trop_walks_sum_def)
qed
(* MOVED FORWARD 2026-04-26: definition was at line 1438, used at line 846+.
Under Isabelle 2025 the use-before-define caused `no_pos_cycle` to be
treated as a schematic free variable, breaking OF resolution at every
call site. *)
definition no_pos_cycle :: "nat \<Rightarrow> trop_mat \<Rightarrow> bool" where
"no_pos_cycle n A \<equiv>
\<forall> i < n. \<forall> k. \<forall> w \<in> walks n k i i. path_weight A w \<le> (1 :: tropical)"
text \<open>
Cycle excision: if a walk has a repeated vertex (i.e.\ contains a cycle),
we can excise the cycle. Under the no-positive-cycle assumption
(@{text no_pos_cycle}), the excised walk has at least as high a weight.
\<close>
(* CORRECTED 2026-04-26: the previous formulation took
`v ∈ set (butlast w)` and `v ∈ set (tl w)` as hypotheses, but those
do NOT pin two distinct positions for v (counter-example: w = [a,v,b]
where v sits at the unique middle position is in both butlast and tl).
OF then found multiple unifiers and the internal pigeonhole could not
close `Suc p0 ≤ q ⟹ Suc p0 < q`. The lemma is now stated directly in
terms of the position pair (p, q), which is what every call site
already has from a `not_distinct_decomp` step. *)
lemma path_weight_cycle_excise:
assumes hp_pos: "0 < p"
assumes hpq: "p < q"
assumes hq_lt: "q < length w"
assumes hpq_eq: "w ! p = w ! q"
assumes hnpc: "no_pos_cycle n A"
assumes hw_in: "w \<in> walks n k i j"
shows "\<exists> w'. path_weight A w' \<ge> path_weight A w \<and>
length w' < length w \<and>
w' \<noteq> [] \<and>
hd w' = hd w \<and> last w' = last w \<and>
set w' \<subseteq> {..<n}"
proof -
define v where v_def: "v = w ! p"
have hp_lt: "p < length w" using hpq hq_lt by simp
(* Bind to the names the original proof body used: p' = p, q' = q. *)
define p' where p'_def: "p' = p"
define q' where q'_def: "q' = q"
have hp': "p' < length w" "w ! p' = v" "0 < p'"
using hp_lt v_def hp_pos by (simp_all add: p'_def)
have hq': "q' < length w" "w ! q' = v"
using hq_lt hpq_eq v_def by (simp_all add: q'_def)
have hpq: "p' < q'" using hpq by (simp add: p'_def q'_def)
(* Witness: excise the cycle between positions p' and q' *)
let ?w' = "take p' w @ drop q' w"
have hw'_hd: "hd ?w' = hd w"
using hp'(3) by (simp add: hd_append take_eq_Nil)
have hw'_last: "last ?w' = last w"
using hq'(1) hw_in unfolding walks_def by (simp add: last_append)
have hw'_len: "length ?w' < length w"
proof -
have "length ?w' = p' + (length w - q')"
using hp'(1) hq'(1) by simp
also have "\<dots> < length w"
using hpq hq'(1) hp'(1) by linarith
finally show ?thesis .
qed
(* Weight bound: the cycle segment has weight ≤ 1 under no_pos_cycle.
The cycle is w[p'..q'] (from v back to v), giving a closed walk.
Since w ∈ walks n k i j, all vertices in w (and hence in the cycle) are in {..<n}.
path_weight A w = path_weight A (take (p'+1) w) * path_weight A (drop p' w)
= path_weight A (take (p'+1) w) * path_weight A ([v] @ cycle-part @ drop q' w)
where cycle-part is from q'+1 onwards.
The cycle weight (path_weight A (take q'-p'+1 (drop p' w))) ≤ 1 by no_pos_cycle.
Thus path_weight A w ≤ path_weight A ?w'.
We now prove this formally. *)
have hw'_weight: "path_weight A w \<le> path_weight A ?w'"
proof -
(* Decompose w at position p': w = take p' w @ drop p' w *)
have take_drop_p: "w = take p' w @ drop p' w" by simp
(* drop p' w is nonempty since p' < length w *)
have drop_p_ne: "drop p' w \<noteq> []"
using hp'(1) by simp
(* take p' w is nonempty since 0 < p' AND p' < length w *)
have take_p_ne: "take p' w \<noteq> []"
using hp'(1) hp'(3) by (cases w) auto
(* hd (drop p' w) = w ! p' = v *)
have hd_drop_p: "hd (drop p' w) = v"
using hp'(1,2) by (simp add: hd_drop_conv_nth)
(* path_weight A w = path_weight A (take p' w @ [v]) * path_weight A (drop p' w) *)
have split_p: "path_weight A w =
path_weight A (take p' w @ [v]) * path_weight A (drop p' w)"
using path_weight_append[OF take_p_ne drop_p_ne]
by (simp add: hd_drop_p)
(* The cycle walk: take (q'-p') (drop p' w) @ [v] *)
let ?C = "take (q'-p') (drop p' w) @ [v]"
have take_C_ne: "take (q'-p') (drop p' w) \<noteq> []"
using hpq hp'(1) by simp
(* drop (q'-p') (drop p' w) = drop q' w *)
have drop_qp_drop_p: "drop (q'-p') (drop p' w) = drop q' w"
using hpq by (simp add: drop_drop)
(* drop q' w is nonempty since q' < length w - 1, so q' + 1 < length w *)
have drop_q_ne: "drop q' w \<noteq> []"
using hq'(1) by simp
(* hd (drop q' w) = w ! q' = v *)
have hd_drop_q: "hd (drop q' w) = v"
using hq'(1) hq'(2)
by (metis hd_drop_conv_nth Suc_less_eq hq'(1))
(* Decompose drop p' w at (q'-p'): drop p' w = take (q'-p') (drop p' w) @ drop q' w *)
have decomp_drop_p: "drop p' w = take (q'-p') (drop p' w) @ drop q' w"
using append_take_drop_id drop_qp_drop_p by metis
(* path_weight A (drop p' w) = path_weight A ?C * path_weight A (drop q' w) *)
have split_q: "path_weight A (drop p' w) =
path_weight A ?C * path_weight A (drop q' w)"
proof -
have "path_weight A (drop p' w) =
path_weight A (take (q'-p') (drop p' w) @ drop q' w)"
using append_take_drop_id drop_qp_drop_p by metis
also have "\<dots> = path_weight A (take (q'-p') (drop p' w) @ [hd (drop q' w)]) *
path_weight A (drop q' w)"
using path_weight_append[OF take_C_ne drop_q_ne] .
also have "\<dots> = path_weight A ?C * path_weight A (drop q' w)"
by (simp add: hd_drop_q)
finally show ?thesis .
qed
(* ?C is a closed walk from v to v: v < n, length = Suc (q'-p'), all verts in {..<n} *)
have v_lt_n: "v < n"
proof -
have "v = w ! p'" using hp'(2) by simp
have "set w \<subseteq> {..<n}" using walk_vertices_bounded[OF hw_in] .
thus ?thesis by (metis \<open>v = w ! p'\<close> in_set_conv_nth hp'(1)
lessThan_iff subsetD)
qed
have C_in_walks: "?C \<in> walks n (q'-p') v v"
proof -
have hlen: "length ?C = Suc (q'-p')"
using hpq hp'(1) hq'(1) by (simp add: min_def)
have hhd: "hd ?C = v"
using hp'(1,2) take_C_ne
by (simp add: hd_append hd_drop_conv_nth)
have hlast: "last ?C = v" by simp
have hset: "set ?C \<subseteq> {..<n}"
proof -
have "set (take (q'-p') (drop p' w)) \<subseteq> set w"
by (meson set_drop_subset set_take_subset subset_trans)
moreover have "set w \<subseteq> {..<n}"
using walk_vertices_bounded[OF hw_in] .
moreover have "{v} \<subseteq> {..<n}" using v_lt_n by simp
ultimately show ?thesis by auto
qed
show ?thesis unfolding walks_def
using hlen hhd hlast hset by simp
qed
(* By no_pos_cycle: path_weight A ?C ≤ 1 *)
have cycle_le: "path_weight A ?C \<le> (1 :: tropical)"
using hnpc v_lt_n C_in_walks
unfolding no_pos_cycle_def by blast
(* Combine: path_weight A (drop p' w) ≤ path_weight A (drop q' w) *)
have drop_p_le: "path_weight A (drop p' w) \<le> path_weight A (drop q' w)"
proof -
have "path_weight A (drop p' w) =
path_weight A ?C * path_weight A (drop q' w)"
using split_q .
also have "\<dots> \<le> 1 * path_weight A (drop q' w)"
using trop_mul_le_mul_right[OF cycle_le] by (simp add: mult.commute)
also have "\<dots> = path_weight A (drop q' w)" by simp
finally show ?thesis .
qed
(* path_weight A w ≤ path_weight A (take p' w @ [v]) * path_weight A (drop q' w) *)
have main_le: "path_weight A w \<le>
path_weight A (take p' w @ [v]) * path_weight A (drop q' w)"
proof -
have "path_weight A w =
path_weight A (take p' w @ [v]) * path_weight A (drop p' w)"
using split_p .
also have "\<dots> \<le> path_weight A (take p' w @ [v]) * path_weight A (drop q' w)"
by (rule trop_mul_le_mul_right[OF drop_p_le])
finally show ?thesis .
qed
(* path_weight A ?w' = path_weight A (take p' w @ [v]) * path_weight A (drop q' w) *)
have w'_eq: "path_weight A ?w' =