Repository navigation
Expand file tree
/
Copy pathMBOAttachment.lean
More file actions
2209 lines (2051 loc) · 117 KB
/
Copy pathMBOAttachment.lean
File metadata and controls
2209 lines (2051 loc) · 117 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
import RandomSystems.Game.MBO
import RandomSystems.Converter.ConverterRelabel
/-!
# Converters attached to games
A converter attaches to a game through its MBO-forwarding lift, this library's definition of a
converter acting on a game: the lift runs the converter on the inside replies without their MBO
and tags each outside reply with the MBO of the latest inside reply. The lift of a DDC from `E`
to `F` is a DDC from `E` to `F`, the lift of a PDC is the PDC of the lifts of a presentation,
and the lift is functorial. Attaching a lift commutes
with the visible system, and attaching it to a game gives a game. A probabilistic
distinguisher with a PDC absorbed wins a game as the distinguisher wins the lift of the PDC
attached to it; so the solver behaviors, the decision probabilities on random systems and the
winning probabilities for games of one distinguisher, are closed under absorbing a PDC.
Sources: CR18, §4.7.2 (printed p. 94), states this action informally: a converter turns a game
`g` into a game `c g`, the MBO being neither seen nor affected by the converter, and a winner `w`
into a winner `w c` with `ω(w c, g) = ω(w, c g)` (equation (4.9)). Definition 4.13 (`R g`) and
the proof of Lemma 4.9 use it.
## Main definitions
* `latestMBO`, `liftMBO`, `PDCBehavior.liftMBO`: the MBO-forwarding lift of a DDC and of a PDC
* `TaggedMBO`: converter transcripts whose outside replies carry the latest inside MBO
* `Domain.SolverBehavior`, `Domain.SolverBehavior.absorb`: solver behaviors
## Main results
* `IsDDCFrom.liftMBO`: the lift of a DDC from `E` to `F` is a DDC from `E` to `F`
* `replies_liftMBO_iff`, `PDCBehavior.liftMBO_apply`: the transcripts of a lift
* `PDCBehavior.liftMBO_id`, `PDCBehavior.liftMBO_comp`: the lift is functorial
* `RandomSystem.visible_attach_liftMBO`: attaching a lift attaches to the visible system
* `RandomSystem.MonotoneMBO.attach_liftMBO`: attaching a lift to a game gives a game
* `Domain.Distinguisher.exists_absorbAll_winProbability`: absorbing a PDC into a winner
-/
namespace SystemAlgebra
open Probability
/-! ## The lift of a DDC -/
section LiftDDC
variable {O J : Type} {U V : O → Type} {X Y : J → Type}
/-- The MBO of an input of a converter: that of an inside reply. -/
def inputMBO : (Σ l, twoFam U (withMBO Y) l) → Option Bool
| ⟨⟨none, _⟩, _⟩ => none
| ⟨⟨some (), _⟩, y⟩ => some y.2
/-- The MBO of the latest inside reply of an input history, unset before any. -/
def latestMBO (h : List (Σ l, twoFam U (withMBO Y) l)) : Bool :=
(h.reverse.findSome? inputMBO).getD false
lemma latestMBO_snoc (h : List (Σ l, twoFam U (withMBO Y) l)) (z : Σ l, twoFam U (withMBO Y) l) :
latestMBO (h ++ [z]) = (inputMBO z).getD (latestMBO h) := by
-- The latest MBO is found by searching the reversed history, which starts with `z`: it is the
-- MBO of `z` if `z` is an inside reply, and the latest MBO of `h` otherwise.
simp only [latestMBO, List.reverse_append, List.reverse_cons, List.reverse_nil, List.nil_append,
List.singleton_append, List.findSome?_cons]
cases inputMBO z <;> rfl
@[simp] lemma latestMBO_nil : latestMBO ([] : List (Σ l, twoFam U (withMBO Y) l)) = false := rfl
/-- **The MBO-forwarding lift** of a converter: it runs the converter on the inside replies
without their MBO, and tags each outside reply with the MBO of the latest inside reply. -/
def liftMBO (a : InsideOutsideSystem O J U V X Y) :
InsideOutsideSystem O J U (withMBO V) X (withMBO Y) :=
-- The lift `liftMBO a`, outside interface on the left, inside interface on the right. An
-- inside reply `(y, b)` reaches `a` as `y`; its MBO `b` bypasses `a` on the MBO wire and is
-- kept as `b₀`, the MBO of the latest inside reply (`latestMBO h`, unset before any). Each
-- outside reply `v` of `a` leaves tagged with `b₀`; inside queries `x` pass untouched.
--
-- ┌──────────────────────────────┐
-- ───── u ────▶ │ ────▶ ┌───┐ ─────── x ─────▶ │ ───── x ─────▶
-- │ │ a │ │
-- ◀── (v, b₀) ─ │ ◀──── └───┘ ◀────── y ────── │ ◀── (y, b) ───
-- │ ▲ │ │
-- │ └──── b₀ ◀──── b ────┘ │
-- └──────────────────────────────┘
-- outside inside
fun h => (a (h.map (insideMap forgetMBO))).map (outsideMap (tagMBO (latestMBO h)))
lemma insideMap_forgetMBO_fst (z : Σ l, twoFam U (withMBO Y) l) :
(insideMap forgetMBO z).1 = z.1 := by
-- Outside query or inside reply: forgetting the MBO keeps the label.
rcases z with ⟨⟨_ | ⟨⟨⟩⟩, j⟩, y⟩ <;> rfl
lemma outsideMap_tagMBO_fst (b : Bool) (y : Σ l, twoFam V X l) :
(outsideMap (tagMBO b) y).1 = y.1 := by
-- Outside reply or inside query: tagging keeps the label.
rcases y with ⟨⟨_ | ⟨⟨⟩⟩, j⟩, y⟩ <;> rfl
lemma outsideMap_forgetMBO_fst (y : Σ l, twoFam (withMBO V) X l) :
(outsideMap forgetMBO y).1 = y.1 := by
-- Outside reply or inside query: forgetting the MBO keeps the label.
rcases y with ⟨⟨_ | ⟨⟨⟩⟩, j⟩, y⟩ <;> rfl
@[simp] lemma outsideMap_forgetMBO_tagMBO (b : Bool) (y : Σ l, twoFam V X l) :
outsideMap forgetMBO (outsideMap (tagMBO b) y) = y := by
-- On an outside reply, forgetting the tag undoes it; an inside query is untouched.
rcases y with ⟨⟨_ | ⟨⟨⟩⟩, j⟩, y⟩ <;> rfl
lemma mem_liftMBO {a : InsideOutsideSystem O J U V X Y} {h} {y} :
y ∈ liftMBO a h ↔ outsideMap forgetMBO y ∈ a (h.map (insideMap forgetMBO)) ∧
y = outsideMap (tagMBO (latestMBO h)) (outsideMap forgetMBO y) := by
-- An output of the lift is the tag of an output `y₀` of `a` on the history without MBOs;
-- forgetting the tag gives back `y₀`.
constructor
-- (→) Write `y` as the tag of `y₀`; forgetting the tag gives `y₀`, and tagging it again `y`.
· intro hy
obtain ⟨y₀, hy₀, rfl⟩ := (Part.mem_map_iff _).mp hy
simpa using hy₀
-- (←) `y` is the tag of the output `y` without its MBO.
· rintro ⟨hy, he⟩
exact (Part.mem_map_iff _).mpr ⟨_, hy, he.symm⟩
lemma liftMBO_dom (a : InsideOutsideSystem O J U V X Y) (h) :
(liftMBO a h).Dom ↔ (a (h.map (insideMap forgetMBO))).Dom := Iff.rfl
/-- **The lift without its outside MBOs** is the converter run on the inside replies without
their MBO. -/
lemma relabel_liftMBO (a : InsideOutsideSystem O J U V X Y) :
relabel (liftMBO a) id (outsideMap forgetMBO) = relabel a (insideMap forgetMBO) id := by
-- Cutting the MBO wire in the picture at `liftMBO`: on each history, both sides run `a` on the
-- history without inside MBOs, and on the left the tag is added and forgotten again.
funext h
simp only [relabel, liftMBO, List.map_id, Part.map_map]
-- It remains that forgetting the tag after tagging is the identity on outputs.
congr 1
funext y
simp
lemma replyLabel_liftMBO (a : InsideOutsideSystem O J U V X Y) (h) :
replyLabel (liftMBO a) h = replyLabel a (h.map (insideMap forgetMBO)) := by
-- The lift replies exactly when `a` does on the history without MBOs (`liftMBO_dom`), and
-- tagging keeps the label of the reply.
unfold replyLabel
by_cases hd : (a (h.map (insideMap forgetMBO))).Dom
-- Both reply, with the same label.
· rw [dif_pos hd, dif_pos ((liftMBO_dom a h).mpr hd)]
exact congrArg some (outsideMap_tagMBO_fst _ _)
-- Neither replies.
· rw [dif_neg hd, dif_neg (fun h' => hd ((liftMBO_dom a h).mp h'))]
lemma lastOuter_insideMap_forgetMBO (h : List (Σ l, twoFam U (withMBO Y) l)) :
lastOuter (h.map (insideMap forgetMBO)) = lastOuter h := by
-- Induction from the right: forgetting the MBO of an inside reply changes no label, and an
-- outside query is kept as it is.
induction h using List.reverseRecOn with
| nil => rfl
| append_singleton h z ih =>
rw [List.map_append, List.map_singleton, lastOuter_snoc, lastOuter_snoc, ih]
rcases z with ⟨⟨_ | ⟨⟨⟩⟩, j⟩, y⟩ <;> rfl
lemma insideRun_liftMBO (a : InsideOutsideSystem O J U V X Y) (h) :
insideRun (liftMBO a) h = insideRun a (h.map (insideMap forgetMBO)) := by
-- `insideRun` counts the consecutive inside queries at the end. Induction from the right: one
-- more input adds one to the count exactly when the output after it is an inside query, and
-- the lift outputs an inside query exactly when `a` does on the history without MBOs (the tag
-- touches outside replies only).
induction h using List.reverseRecOn with
| nil => rfl
| append_singleton h z ih =>
-- Unfold one step of the count on both sides and use the induction hypothesis.
rw [insideRun, run_snoc, List.map_append, List.map_singleton, insideRun, run_snoc, ← insideRun,
← insideRun, ih]
-- It remains that both answer `h ++ [z]` with an inside query at the same time.
congr 1
apply propext
constructor
-- (→) An inside query of the lift is the tag of an inside query of `a`, with the same label.
· rintro ⟨y, hy, hi⟩
exact ⟨_, by simpa only [List.map_append, List.map_singleton] using (mem_liftMBO.mp hy).1, by
rw [IsInside, outsideMap_forgetMBO_fst]; exact hi⟩
-- (←) Tag the inside query `y` of `a`; tagging keeps its label.
· rintro ⟨y, hy, hi⟩
refine ⟨outsideMap (tagMBO (latestMBO (h ++ [z]))) y,
(Part.mem_map_iff _).mpr ⟨y, by simpa using hy, rfl⟩, ?_⟩
rw [IsInside, outsideMap_tagMBO_fst]
exact hi
/-- The lift of a DDC is a DDC with the same bound. -/
lemma IsDDC.liftMBO {b : ℕ} {a : InsideOutsideSystem O J U V X Y} (ha : IsDDC b a) :
IsDDC b (SystemAlgebra.liftMBO a) where
-- Silent at the empty history and prefix closed: both are statements about domains, and the
-- lift has the domain of `a` on the history without MBOs; prefixes map to prefixes.
dds := ⟨fun hd => ha.dds.1 hd, fun l₁ l₂ hp hne hd =>
ha.dds.2 (hp.map _) (by simpa using hne) hd⟩
-- Admission: `a` admits the input without its MBO after the history without MBOs; the reply
-- labels of the lift are those of `a` (`replyLabel_liftMBO`), and forgetting keeps labels.
admits h z hd := by
have := ha.admits (h.map (insideMap forgetMBO)) (insideMap forgetMBO z)
(by simpa only [liftMBO_dom, List.map_append, List.map_singleton] using hd)
rw [Admits, ← replyLabel_liftMBO, insideMap_forgetMBO_fst] at this
exact this
-- The inside run of the lift is that of `a` (`insideRun_liftMBO`), bounded by `b`.
bound h hd := by
rw [insideRun_liftMBO]
exact ha.bound _ hd
-- An outside reply of the lift at `o` comes from an outside reply of `a` at `o`, which follows
-- an outside query at `o`; forgetting inside MBOs keeps the latest outside label.
replies h y o hy hl := by
have := ha.replies _ _ o (mem_liftMBO.mp hy).1 (by rw [outsideMap_forgetMBO_fst]; exact hl)
rwa [lastOuter_insideMap_forgetMBO] at this
/-- A converter transcript with the MBOs of its inside replies and outside replies forgotten. -/
def forgetConverterMBO
(h : List ((Σ l, twoFam U (withMBO Y) l) × (Σ l, twoFam (withMBO V) X l))) :
List ((Σ l, twoFam U Y l) × (Σ l, twoFam V X l)) :=
h.map (Prod.map (insideMap forgetMBO) (outsideMap forgetMBO))
/-- **The outside replies of a converter transcript carry the latest inside MBO.** -/
def TaggedMBO
(h : List ((Σ l, twoFam U (withMBO Y) l) × (Σ l, twoFam (withMBO V) X l))) : Prop :=
∀ p z t, h = p ++ z :: t →
z.2 = outsideMap (tagMBO (latestMBO (p.map Prod.fst ++ [z.1]))) (outsideMap forgetMBO z.2)
lemma taggedMBO_snoc
{h : List ((Σ l, twoFam U (withMBO Y) l) × (Σ l, twoFam (withMBO V) X l))} {z} :
TaggedMBO (h ++ [z]) ↔ TaggedMBO h ∧
z.2 = outsideMap (tagMBO (latestMBO (h.map Prod.fst ++ [z.1])))
(outsideMap forgetMBO z.2) := by
-- The split points `p ++ z' :: t` of `h ++ [z]` are those of `h`, with `[z]` added to `t`,
-- and the split point at `z` itself.
constructor
-- (→) Restrict to the split points in `h`, and take the split point at `z`.
· intro ht
exact ⟨fun p w t he => ht p w (t ++ [z]) (by rw [he]; simp), ht h z [] rfl⟩
-- (←) Case on the tail `t` of a split point of `h ++ [z]`.
· rintro ⟨ht, hz⟩ p w t he
rcases List.eq_nil_or_concat t with rfl | ⟨t, v, rfl⟩
-- Empty tail: the split point is at `z`.
· have he' : h ++ [z] = p ++ [w] := he
obtain ⟨rfl, hw⟩ := List.append_inj' he' rfl
obtain rfl : z = w := by simpa using hw
exact hz
-- Tail `t ++ [v]`: the split point lies in `h`.
· rw [List.concat_eq_append] at he
have he' : h ++ [z] = (p ++ w :: t) ++ [v] := by rw [he]; simp
obtain ⟨rfl, -⟩ := List.append_inj' he' rfl
exact ht p w t rfl
/-- **The transcripts of a lift** are the transcripts of the converter with the MBOs forgotten
whose outside replies carry the latest inside MBO. -/
lemma replies_liftMBO_iff (a : InsideOutsideSystem O J U V X Y)
(h : List ((Σ l, twoFam U (withMBO Y) l) × (Σ l, twoFam (withMBO V) X l))) :
Replies (liftMBO a) (h.map Prod.fst) (h.map Prod.snd) ↔
Replies a ((forgetConverterMBO h).map Prod.fst) ((forgetConverterMBO h).map Prod.snd) ∧
TaggedMBO h := by
-- Induction from the right on the transcript. One more round `z` extends a transcript of the
-- lift iff the lift answers `z.1` with `z.2`, which (`mem_liftMBO`) says that `a` answers `z.1`
-- without its MBO with `z.2` without its MBO, and that `z.2` carries the latest inside MBO.
induction h using List.reverseRecOn with
-- The empty transcript: both sides hold.
| nil => simp [forgetConverterMBO, TaggedMBO]
| append_singleton h z ih =>
-- Split off the last round on both sides and use the induction hypothesis.
simp only [forgetConverterMBO, List.map_append, List.map_singleton, replies_snoc,
taggedMBO_snoc] at ih ⊢
rw [ih]
-- The last round, by `mem_liftMBO`.
have hz : liftMBO a (h.map Prod.fst ++ [z.1]) = Part.some z.2 ↔
a ((h.map (Prod.map (insideMap forgetMBO) (outsideMap forgetMBO))).map Prod.fst ++
[(Prod.map (insideMap forgetMBO) (outsideMap forgetMBO) z).1]) =
Part.some (Prod.map (insideMap forgetMBO) (outsideMap forgetMBO) z).2 ∧
z.2 = outsideMap (tagMBO (latestMBO (h.map Prod.fst ++ [z.1])))
(outsideMap forgetMBO z.2) := by
rw [Part.eq_some_iff, Part.eq_some_iff, mem_liftMBO]
simp only [List.map_append, List.map_map, List.map_singleton, Prod.map_fst, Prod.map_snd]
rfl
-- Regroup the conjunctions.
rw [hz]
tauto
/-- Forgetting the MBOs keeps the labels of a converter transcript. -/
lemma admitted_forgetConverterMBO
(h : List ((Σ l, twoFam U (withMBO Y) l) × (Σ l, twoFam (withMBO V) X l))) :
Admitted converterAdmits (forgetConverterMBO h) ↔ Admitted converterAdmits h :=
-- Forgetting MBOs relabels inputs and outputs and keeps every label (the label map is the
-- identity), so it keeps admission (`admitted_map_iff`).
admitted_map_iff (insideMap forgetMBO) (outsideMap forgetMBO) id id Function.injective_id
(fun z => by rw [insideMap_forgetMBO_fst]; rcases z with ⟨⟨_ | ⟨⟨⟩⟩, j⟩, y⟩ <;> rfl)
(fun z => by rw [outsideMap_forgetMBO_fst]; rcases z with ⟨⟨_ | ⟨⟨⟩⟩, j⟩, y⟩ <;> rfl) h
lemma outsideInputs_insideMap_forgetMBO (xs : List (Σ l, twoFam U (withMBO Y) l)) :
outsideInputs (xs.map (insideMap forgetMBO)) = outsideInputs xs := by
-- Induction from the right: an outside query is kept as it is, and an inside reply adds no
-- outside input, with or without its MBO.
induction xs using List.reverseRecOn with
| nil => rfl
| append_singleton xs x ih =>
rw [List.map_append, outsideInputs_append, outsideInputs_append, ih]
congr 1
-- Case on the last input: outside query or inside reply.
rcases x with ⟨⟨_ | ⟨⟨⟩⟩, o⟩, u⟩ <;>
simp [outsideInputs, insideMap, splitIn, restrict_cons_self, restrict_cons_ne]
variable {E : List (Σ j, X j) → Prop} {F : List (Σ o, U o) → Prop}
/-- Forgetting the MBOs keeps the converter domain. -/
lemma converterDomain_forgetConverterMBO
(h : List ((Σ l, twoFam U (withMBO Y) l) × (Σ l, twoFam (withMBO V) X l)))
(x : Σ l, twoFam U (withMBO Y) l) :
converterDomain E F (forgetConverterMBO h) (insideMap forgetMBO x) ↔
converterDomain E F h x := by
-- The converter domain is built from admission, the outside inputs and the inside queries;
-- forgetting the MBOs keeps each of them. Admission is kept, since labels are kept.
have hadm : converterAdmits (forgetConverterMBO h) (insideMap forgetMBO x) ↔
converterAdmits h x :=
converterAdmits_map_iff (insideMap forgetMBO) (outsideMap forgetMBO) id id Function.injective_id
(fun z => by rw [insideMap_forgetMBO_fst]; rcases z with ⟨⟨_ | ⟨⟨⟩⟩, j⟩, y⟩ <;> rfl)
(fun z => by rw [outsideMap_forgetMBO_fst]; rcases z with ⟨⟨_ | ⟨⟨⟩⟩, j⟩, y⟩ <;> rfl) h x
-- The outside inputs are kept (`outsideInputs_insideMap_forgetMBO`).
have hout : outsideInputs ((forgetConverterMBO h).map Prod.fst ++ [insideMap forgetMBO x]) =
outsideInputs (h.map Prod.fst ++ [x]) := by
rw [← outsideInputs_insideMap_forgetMBO (h.map Prod.fst ++ [x])]
simp [forgetConverterMBO, List.map_map, Function.comp_def]
-- The inside queries are kept: they carry no MBO.
have hin : insideQueries ((forgetConverterMBO h).map Prod.snd) =
insideQueries (h.map Prod.snd) := by
rw [← insideQueries_outsideMap forgetMBO (h.map Prod.snd)]
simp [forgetConverterMBO, List.map_map, Function.comp_def]
-- Combine the three.
simp only [converterDomain, admitted_forgetConverterMBO, hadm, hout, hin]
/-- **The lift of a DDC from `E` to `F`** is a DDC from `E` to `F`. -/
lemma IsDDCFrom.liftMBO {b : ℕ} {a : InsideOutsideSystem O J U V X Y}
(ha : IsDDCFrom E F b a) : IsDDCFrom E F b (SystemAlgebra.liftMBO a) where
-- The DDC conditions: `IsDDC.liftMBO`.
isDDC := ha.isDDC.liftMBO
-- The lift answers exactly in the converter domain: its transcript is a transcript of `a` once
-- the MBOs are forgotten (`replies_liftMBO_iff`), where `a` answers exactly in the domain, and
-- forgetting keeps the domain (`converterDomain_forgetConverterMBO`).
answers h x hr := by
have hr' := ((replies_liftMBO_iff a h).mp hr).1
rw [liftMBO_dom, ← converterDomain_forgetConverterMBO, ← ha.answers _ _ hr']
simp [forgetConverterMBO, List.map_map, Function.comp_def]
-- The inside queries stay in `E`: they are those of `a` on the transcript without MBOs.
queries h hr := by
have := ha.queries _ ((replies_liftMBO_iff a h).mp hr).1
have hin : insideQueries ((forgetConverterMBO h).map Prod.snd) =
insideQueries (h.map Prod.snd) := by
rw [← insideQueries_outsideMap forgetMBO (h.map Prod.snd)]
simp [forgetConverterMBO, List.map_map, Function.comp_def]
rwa [hin] at this
open scoped Classical in
/-- Lifting commutes with restricting to the outside inputs in a domain. -/
lemma liftMBO_filterDom (P : List (Σ o, U o) → Prop) (a : InsideOutsideSystem O J U V X Y) :
liftMBO (filterDom (fun h => P (outsideInputs h)) a) =
filterDom (fun h => P (outsideInputs h)) (liftMBO a) := by
-- The restriction tests the outside inputs, which forgetting the inside MBOs keeps; the tag
-- passes through the `if`.
funext h
simp only [liftMBO, filterDom, outsideInputs_insideMap_forgetMBO]
split_ifs <;> simp
lemma admissible_liftMBO_iff (a : InsideOutsideSystem O J U V X Y) (h) :
Admissible (liftMBO a) h ↔ Admissible a (h.map (insideMap forgetMBO)) := by
-- Induction from the right: admissibility of one more input asks for a defined reply before it
-- and for its admission; both are the same for the lift and for `a` without MBOs.
induction h using List.reverseRecOn with
| nil => simp
| append_singleton h z ih =>
rw [admissible_snoc, List.map_append, List.map_singleton, admissible_snoc, ih, Admits, Admits,
replyLabel_liftMBO, insideMap_forgetMBO_fst, liftMBO_dom]
simp
open scoped Classical in
/-- Lifting commutes with restricting a converter to its admissible histories. -/
lemma liftMBO_canon (a : InsideOutsideSystem O J U V X Y) :
liftMBO (canon a) = canon (liftMBO a) := by
-- `canon` restricts to admissible histories, and the lift is admissible exactly where `a` is
-- on the history without MBOs (`admissible_liftMBO_iff`).
funext h
simp only [liftMBO, canon, admissible_liftMBO_iff]
split_ifs <;> simp
/-- **The lift of the forwarding converter** is the forwarding converter: an outside reply
forwards the latest inside reply with its MBO. -/
lemma liftMBO_forwardAll : liftMBO (forwardAll J X Y) = forwardAll J X (withMBO Y) := by
-- The forwarding converter forwards the last input. Case on the history.
funext h
rcases List.eq_nil_or_concat h with rfl | ⟨h, z, rfl⟩
-- Empty history: both are silent.
· simp [liftMBO, forwardAll]
· rw [List.concat_eq_append]
-- Case on the last input.
rcases z with ⟨⟨_ | ⟨⟨⟩⟩, j⟩, y⟩
-- An outside query is forwarded as an inside query, which the lift leaves untagged.
· simp only [liftMBO, List.map_append, List.map_singleton]
change Part.map _ (forwardAll J X Y (_ ++ [⟨⟨none, j⟩, y⟩])) = _
rw [forwardAll_snoc_outer, forwardAll_snoc_outer]
rfl
-- An inside reply `(y, b)` is forwarded as the outside reply `y`, tagged with the latest inside
-- MBO, which is `b` (`latestMBO_snoc`): the forwarded reply is `(y, b)`.
· simp only [liftMBO, List.map_append, List.map_singleton]
change Part.map _ (forwardAll J X Y (_ ++ [⟨⟨some (), j⟩, y.1⟩])) = _
rw [forwardAll_snoc_inner, forwardAll_snoc_inner, latestMBO_snoc]
rfl
/-- **The lift of a filter** is the filter on replies with the MBO. -/
lemma liftMBO_filter (D : List (Σ j, X j) → Prop) (hD : ∀ {p h}, p <+: h → D h → D p) :
liftMBO (DDC.filter (Y := Y) D hD).1 = (DDC.filter (Y := withMBO Y) D hD).1 := by
-- The filter is the forwarding converter made canonical and restricted on its outside inputs;
-- the lift commutes with each of the three.
change liftMBO (filterDom _ (canon (forwardAll J X Y))) = filterDom _ (canon _)
rw [liftMBO_filterDom, liftMBO_canon, liftMBO_forwardAll]
end LiftDDC
/-! ## The lift of a PDC -/
section LiftPDC
variable {O J : Type} [Fintype O] [Fintype J] {U V : O → Type} {X Y : J → Type}
[∀ o, Fintype (U o)] [∀ o, Fintype (V o)] [∀ j, Fintype (X j)] [∀ j, Fintype (Y j)]
{E : List (Σ j, X j) → Prop} {m : ℕ} {hE : ∀ h, E h → h.length ≤ m}
{F : List (Σ o, U o) → Prop} {n : ℕ} {hF : ∀ h, F h → h.length ≤ n}
/-- The lifts of the DDCs of a presentation. -/
noncomputable def liftPresentation {b : ℕ}
(P : Distribution.ProbDist {a : InsideOutsideSystem O J U V X Y // IsDDCFrom E F b a}) :
Distribution.ProbDist
{c : InsideOutsideSystem O J U (withMBO V) X (withMBO Y) // IsDDCFrom E F b c} :=
⟨Distribution.fTransform (fun a => ⟨liftMBO a.1, a.2.liftMBO⟩) P.1,
Distribution.fTransform_isProbDist _ P.2⟩
omit [Fintype O] [Fintype J] [∀ o, Fintype (U o)] [∀ o, Fintype (V o)] [∀ j, Fintype (X j)]
[∀ j, Fintype (Y j)] in
open scoped Classical in
/-- On a converter transcript, the lifted presentation gives the mass of the transcript with the
MBOs forgotten when its outside replies carry the latest inside MBO, and zero otherwise. -/
lemma behaviorMass_liftPresentation {b : ℕ}
(P : Distribution.ProbDist {a : InsideOutsideSystem O J U V X Y // IsDDCFrom E F b a})
(h : List ((Σ l, twoFam U (withMBO Y) l) × (Σ l, twoFam (withMBO V) X l))) :
behaviorMass (liftPresentation P).1 (h.map Prod.fst) (h.map Prod.snd) =
if TaggedMBO h then
behaviorMass P.1 ((forgetConverterMBO h).map Prod.fst) ((forgetConverterMBO h).map Prod.snd)
else 0 := by
-- The mass of a transcript is the probability that the sampled DDC has it. The lift of `a` has
-- it iff `a` has it without MBOs and it is tagged (`replies_liftMBO_iff`), and taggedness does
-- not depend on `a`.
simp only [behaviorMass, liftPresentation, Distribution.mass_fTransform, replies_liftMBO_iff]
split_ifs with ht
-- Tagged: only the condition on `a` remains.
· exact Distribution.mass_congr _ fun a => and_iff_left ht
-- Not tagged: no lift has the transcript.
· exact Distribution.mass_eq_zero_of_forall_not _ fun a ha => ht ha.2
/-- **The MBO-forwarding lift** of a PDC: the PDC of the lifts of a presentation. -/
noncomputable def PDCBehavior.liftMBO (α : PDCBehavior O J U V X Y E m hE F n hF) :
PDCBehavior O J U (withMBO V) X (withMBO Y) E m hE F n hF :=
PDCBehavior.ofPDC (liftPresentation α.2.choose_spec.choose)
open scoped Classical in
/-- **The transcripts of a lifted PDC**: the mass of a transcript with the MBOs forgotten, when
its outside replies carry the latest inside MBO. -/
lemma PDCBehavior.liftMBO_apply (α : PDCBehavior O J U V X Y E m hE F n hF)
(h : List ((Σ l, twoFam U (withMBO Y) l) × (Σ l, twoFam (withMBO V) X l))) :
(PDCBehavior.liftMBO α).1 h = if TaggedMBO h then α.1 (forgetConverterMBO h) else 0 := by
-- The lift is defined from a chosen presentation of `α`; compute on it
-- (`behaviorMass_liftPresentation`) and use that it presents `α`.
change behaviorMass (liftPresentation α.2.choose_spec.choose).1 _ _ = _
rw [behaviorMass_liftPresentation, α.2.choose_spec.choose_spec]
/-- Any presentation of a PDC lifts to a presentation of its lift. -/
lemma PDCBehavior.liftMBO_presents (α : PDCBehavior O J U V X Y E m hE F n hF) {b : ℕ}
(P : Distribution.ProbDist {a : InsideOutsideSystem O J U V X Y // IsDDCFrom E F b a})
(hP : ∀ h, behaviorMass P.1 (h.map Prod.fst) (h.map Prod.snd) = α.1 h) (h) :
behaviorMass (liftPresentation P).1 (h.map Prod.fst) (h.map Prod.snd) =
(PDCBehavior.liftMBO α).1 h := by
-- Both sides are the tagged mass of `α` (`behaviorMass_liftPresentation`, `liftMBO_apply`).
rw [behaviorMass_liftPresentation, hP, PDCBehavior.liftMBO_apply]
open scoped Classical in
/-- The lift of the behavior of a DDC is the behavior of its lift. -/
lemma PDCBehavior.liftMBO_ofDDC (a : InsideOutsideSystem O J U V X Y) {b : ℕ}
(ha : IsDDCFrom E F b a) :
PDCBehavior.liftMBO (PDCBehavior.ofDDC a ha : PDCBehavior O J U V X Y E m hE F n hF) =
PDCBehavior.ofDDC (SystemAlgebra.liftMBO a) ha.liftMBO := by
-- Compare transcript by transcript: the behavior of a DDC is the indicator of its transcripts,
-- and the transcripts of the lift are the tagged ones that are transcripts of `a` without MBOs.
apply PDCBehavior.ext
intro h
rw [PDCBehavior.liftMBO_apply, PDCBehavior.ofDDC_apply, PDCBehavior.ofDDC_apply,
replies_liftMBO_iff]
-- Split on whether the transcript is tagged.
by_cases ht : TaggedMBO h <;> simp [ht, forgetConverterMBO]
/-- **The lift of the identity** is the identity. -/
lemma PDCBehavior.liftMBO_id (hE' : ¬ E [] ∧ ∀ {p h}, p <+: h → p ≠ [] → E h → E p) :
PDCBehavior.liftMBO (PDCBehavior.id (Y := Y) (m := m) (hE := hE) hE') =
PDCBehavior.id (Y := withMBO Y) hE' := by
-- The identity PDC is the behavior of the filter DDC on `h = [] ∨ E h`, and the lift of the
-- behavior of a DDC is the behavior of the lifted DDC (`liftMBO_ofDDC`).
rw [PDCBehavior.id, PDCBehavior.liftMBO_ofDDC]
apply PDCBehavior.ext
intro h
-- The lift of the filter is the filter on replies with the MBO (`liftMBO_filter`).
rw [PDCBehavior.id, PDCBehavior.ofDDC_apply, PDCBehavior.ofDDC_apply,
liftMBO_filter _ (prefix_or_nil hE'.2)]
end LiftPDC
/-! ## Attaching a lift, and the visible system -/
section Visible
variable {O J : Type} {U V : O → Type} {X Y : J → Type}
/-- Relabeling the outside replies of an attachment relabels those of the converter. -/
lemma relabel_apply_outside {V' : O → Type} (c : InsideOutsideSystem O J U V' X Y)
(R : InterfaceSystem J X Y) (g : (Σ o, V' o) → (Σ o, V o)) :
relabel (apply c R) id g = apply (relabel c id (outsideMap g)) R := by
-- Write both attachments as interconnections of `[c, R]`; relabeling the outside replies of
-- the attachment, or of `c`, moves into the routing table.
rw [apply_eq, apply_eq, relabel_interconnect, pair_relabel_left, interconnect_relabel]
-- Only the routing tables differ; compare them output by output.
congr 1
funext y
-- Outputs of `c` (outside reply or inside query) and replies of `R`.
rcases y with ⟨_ | ⟨⟩, y⟩
· rcases y with ⟨⟨_ | ⟨⟨⟩⟩, o⟩, v⟩ <;> rfl
· rfl
/-- A converter relabeling its inside replies, attached to a resource, is the converter attached
to the resource with its replies relabeled. -/
lemma apply_relabel_inside {Y' : J → Type} (c : InsideOutsideSystem O J U V X Y)
(R : InterfaceSystem J X Y') (k : (Σ j, Y' j) → (Σ j, Y j)) :
apply (relabel c (insideMap k) id) R = apply c (relabel R id k) := by
-- Write both attachments as interconnections of `[c, R]`; relabeling the inputs of `c`, or the
-- replies of `R`, moves into the routing table and the injection.
rw [apply_eq, apply_eq, pair_relabel_left, interconnect_relabel, pair_relabel_right,
interconnect_relabel]
-- The routing tables agree output by output; the injections agree.
congr 1
· funext y
rcases y with ⟨_ | ⟨⟩, y⟩
· rcases y with ⟨⟨_ | ⟨⟨⟩⟩, o⟩, v⟩ <;> rfl
· rfl
/-- **Attaching a lift and forgetting the MBO** is attaching the converter to the system with the
MBO forgotten. -/
lemma relabel_trim_apply_liftMBO (a : InsideOutsideSystem O J U V X Y)
(s : InterfaceSystem J X (withMBO Y)) :
relabel (trim (apply (liftMBO a) s)) id forgetMBO =
trim (apply a (relabel s id forgetMBO)) := by
-- Forgetting the outside MBO `b₀` of the left system gives the right one (`s⁻` is `s` with its
-- MBO forgotten):
--
-- ┌───────────┐ ──── x ────▶ ┌───┐ ┌───┐ ──── x ────▶ ┌────┐
-- ─u─▶ │ liftMBO a │ │ s │ ─u─▶ │ a │ │ s⁻ │
-- ◀─── └───────────┘ ◀── (y, b) ─ └───┘ ◀v── └───┘ ◀──── y ──── └────┘
-- (v, b₀)
--
-- Move `trim` outside, move the outside relabeling into the lift (`relabel_apply_outside`), cut
-- the MBO wire of the lift (`relabel_liftMBO`), and move the inside relabeling into `s`
-- (`apply_relabel_inside`).
rw [← trim_relabel, relabel_apply_outside, relabel_liftMBO, apply_relabel_inside]
variable [Fintype O] [Fintype J] [∀ o, Fintype (U o)] [∀ o, Fintype (V o)]
[∀ j, Fintype (X j)] [∀ j, Fintype (Y j)]
{E : List (Σ j, X j) → Prop} {m : ℕ} {hE : ∀ h, E h → h.length ≤ m}
{F : List (Σ o, U o) → Prop} {n : ℕ} {hF : ∀ h, F h → h.length ≤ n}
omit [Fintype O] [∀ o, Fintype (U o)] [∀ o, Fintype (V o)] in
/-- The visible systems of a presentation of a game. -/
noncomputable def visiblePresentation
(Q : Distribution.ProbDist {s : InterfaceSystem J X (withMBO Y) //
IsDDS s ∧ RepliesAtQueriedInterface s ∧ ∀ h, (s h).Dom ↔ E h}) :
Distribution.ProbDist {s : InterfaceSystem J X Y //
IsDDS s ∧ RepliesAtQueriedInterface s ∧ ∀ h, (s h).Dom ↔ E h} :=
-- Push `Q` forward by forgetting the MBO; forgetting keeps the DDS conditions, the replies at
-- the queried interface and the domain.
⟨Distribution.fTransform (fun s => ⟨relabel s.1 id forgetMBO,
⟨fun hd => s.2.1.1 (by simpa [relabel] using hd),
fun _ _ hp hne hd => by
simp only [relabel, List.map_id, Part.map_Dom] at hd ⊢
exact s.2.1.2 hp hne hd⟩,
s.2.2.1.relabel id forgetMBO (fun _ _ h => h),
fun h => by simpa [relabel] using s.2.2.2 h⟩) Q.1,
Distribution.fTransform_isProbDist _ Q.2⟩
omit [Fintype O] [∀ o, Fintype (U o)] [∀ o, Fintype (V o)] in
open scoped Classical in
/-- The visible systems of a presentation of a game present its visible system. -/
lemma behaviorMass_visiblePresentation
(Q : Distribution.ProbDist {s : InterfaceSystem J X (withMBO Y) //
IsDDS s ∧ RepliesAtQueriedInterface s ∧ ∀ h, (s h).Dom ↔ E h})
{G : RandomSystem (Σ j, X j) (Σ j, withMBO Y j) (Domain.ofInputs E m hE)}
(hQ : ∀ h, behaviorMass Q.1 (h.map Prod.fst) (h.map Prod.snd) = G h)
(h : List ((Σ j, X j) × Σ j, Y j)) :
behaviorMass (visiblePresentation Q).1 (h.map Prod.fst) (h.map Prod.snd) = G.visible h := by
-- `G⁻ h` is the sum of `G` over the MBOs of the replies in `h`.
rw [RandomSystem.visible_apply]
-- Expand `G` through its presentation `Q`: the sum over the support of `Q` of the weight of `s`
-- times the indicator that `s` has the transcript.
have hG : (G : List ((Σ j, X j) × Σ j, withMBO Y j) → ℝ) =
fun k => ∑ s ∈ Q.1.support, Q.1 s *
if Replies s.1 (k.map Prod.fst) (k.map Prod.snd) then 1 else 0 := by
funext k
rw [← hQ k, behaviorMass, Distribution.mass, Finsupp.sum]
exact Finset.sum_congr rfl fun s _ => by split_ifs <;> simp
-- Sum over the MBOs inside the sum over `s`; the weight factors out, and the sum of the
-- indicator over the MBOs is the indicator for `s` with its MBO forgotten (`sumMBO_replies`).
rw [hG, sumMBO_sum]
simp only [sumMBO_const_mul, sumMBO_replies]
-- The pushed-forward presentation gives the same sum.
rw [visiblePresentation, behaviorMass, Distribution.mass_fTransform, Distribution.mass,
Finsupp.sum]
exact Finset.sum_congr rfl fun s _ => by split_ifs <;> simp
open scoped Classical in
/-- **Attaching a lift attaches to the visible system**: `(R g)⁻ = R g⁻`. -/
theorem RandomSystem.visible_attach_liftMBO
(hE' : ¬ E [] ∧ ∀ {p h}, p <+: h → p ≠ [] → E h → E p)
(hF' : ¬ F [] ∧ ∀ {p h}, p <+: h → p ≠ [] → F h → F p)
(α : PDCBehavior O J U V X Y E m hE F n hF)
(G : RandomSystem (Σ j, X j) (Σ j, withMBO Y j) (Domain.ofInputs E m hE))
(hG : G.RepliesAtQueriedInterface) :
(PDCBehavior.attach hE' hF' (PDCBehavior.liftMBO α) G hG).visible =
PDCBehavior.attach hE' hF' α G.visible hG.visible := by
-- Forgetting the outside MBO `b₀` of the left system gives the right one:
--
-- ┌───────────┐ ──── x ────▶ ┌───┐ ┌───┐ ──── x ────▶ ┌────┐
-- ─u─▶ │ liftMBO α │ │ G │ ─u─▶ │ α │ │ G⁻ │
-- ◀─── └───────────┘ ◀── (y, b) ─ └───┘ ◀v── └───┘ ◀──── y ──── └────┘
-- (v, b₀)
--
-- Plan:
-- 1. present `α` by DDCs (`P`) and `G` by DDSs (`Q`);
-- 2. the right side is a mass over `P × Q⁻` (`Q⁻`: `Q` with the MBO forgotten), i.e. over
-- `P × Q` of the transcripts of `trim (apply a s⁻)`;
-- 3. the left side, before taking the visible system, is a sum over `P × Q` of weights times
-- the indicators of the transcripts of `trim (apply (liftMBO a) s)`;
-- 4. summing over the MBOs passes through the sum and the weights, and turns each indicator
-- into the one with the outside MBO forgotten, which is `trim (apply a s⁻)`
-- (`relabel_trim_apply_liftMBO`);
-- 5. compare term by term.
-- Step 1.
obtain ⟨b, P, hP⟩ := α.2
obtain ⟨Q, hQ⟩ := RandomSystem.exists_resource_presentation hE' G hG
-- Step 2.
ext h
rw [RandomSystem.visible_apply, PDCBehavior.attach_presents hE' hF' α G.visible hG.visible P
(visiblePresentation Q) hP (behaviorMass_visiblePresentation Q hQ) h,
behaviorMass_attachPDS, visiblePresentation, Distribution.fTransform_prod_right,
Distribution.mass_fTransform]
-- Step 3.
have hL : (PDCBehavior.attach hE' hF' (PDCBehavior.liftMBO α) G hG :
List ((Σ o, U o) × Σ o, withMBO V o) → ℝ) = fun k =>
∑ st ∈ (Distribution.prod P.1 Q.1).support, Distribution.prod P.1 Q.1 st *
if Replies (trim (apply (liftMBO st.1.1) st.2.1)) (k.map Prod.fst) (k.map Prod.snd)
then 1 else 0 := by
funext k
rw [PDCBehavior.attach_presents hE' hF' _ G hG (liftPresentation P) Q
(PDCBehavior.liftMBO_presents α P hP) hQ k, behaviorMass_attachPDS, liftPresentation,
Distribution.fTransform_prod_left, Distribution.mass_fTransform, Distribution.mass,
Finsupp.sum]
refine Finset.sum_congr rfl fun st _ => ?_
change (if Replies (trim (apply (liftMBO st.1.1) st.2.1)) _ _ then _ else _) = _
split_ifs <;> simp
-- Step 4.
rw [hL, sumMBO_sum]
simp only [sumMBO_const_mul, sumMBO_replies, relabel_trim_apply_liftMBO]
-- Step 5.
rw [Distribution.mass, Finsupp.sum]
refine Finset.sum_congr rfl fun st _ => ?_
change _ = (if Replies (trim (apply st.1.1 (SystemAlgebra.relabel st.2.1 id forgetMBO))) _ _
then _ else _)
split_ifs <;> simp
end Visible
/-! ## Attaching a lift to a game gives a game -/
section Monotone
variable {O J : Type} {U V : O → Type} {X Y : J → Type}
lemma latestMBO_eq (l : List (Σ l, twoFam U (withMBO Y) l)) :
latestMBO l = ((l.filterMap inputMBO).getLast?).getD false := by
-- Induction from the right: one more input changes both sides exactly when it is an inside
-- reply, and then both become its MBO.
induction l using List.reverseRecOn with
| nil => rfl
| append_singleton l z ih =>
rw [latestMBO_snoc, ih, List.filterMap_append]
rcases z with ⟨⟨_ | ⟨⟨⟩⟩, j⟩, y⟩ <;> simp [inputMBO]
lemma getLast?_getD_le {l : List Bool} {b : Bool} (hp : (l ++ [b]).Pairwise (· ≤ ·)) :
(l.getLast?).getD false ≤ b := by
-- `l` is empty, or ends with `c`.
rcases List.eq_nil_or_concat l with rfl | ⟨l, c, rfl⟩
-- Empty: the default `false` is ≤ `b`.
· exact Bool.false_le b
-- `l = l' ++ [c]`: the last element is `c`, and `c ≤ b` as `c` precedes `b` in the monotone
-- list `l' ++ [c] ++ [b]`.
· rw [List.concat_eq_append] at hp ⊢
rw [List.getLast?_append, List.getLast?_singleton]
simp only [Option.some_or, Option.getD_some]
exact (List.pairwise_append.mp hp).2.2 c (by simp) b (by simp)
/-- The MBO of a reply of the resource that is about to be returned to the converter. -/
def pendingMBO (ys : List (Two (Σ l, twoFam (withMBO V) X l) (Σ j, withMBO Y j))) : List Bool :=
match ys.getLast? with
| some ⟨some (), r⟩ => [r.2.2]
| _ => []
/-- **Along an attachment of a converter that tags its outside replies with the latest inside
MBO**, the resource replies with monotone MBOs as the converter receives them, and the exposed
MBOs are monotone and below the latest inside MBO. -/
lemma attach_tags_invariant {c : InsideOutsideSystem O J U (withMBO V) X (withMBO Y)}
(hc : ∀ h (o : O) (v : withMBO V o), ⟨⟨none, o⟩, v⟩ ∈ c h → v.2 = latestMBO h)
{R : InterfaceSystem J X (withMBO Y)}
(hR : ∀ t : List ((Σ j, X j) × Σ j, withMBO Y j), Replies R (t.map Prod.fst) (t.map Prod.snd) →
(t.map fun z => z.2.2.2).Pairwise (· ≤ ·))
{us : List (Σ o, U o)} {xs ys}
(tr : Transcript (pair c R) (connectionQueries resourceRoute resourceInput us) xs ys) :
Replies R (restrict (some ()) xs) (restrict (some ()) ys) ∧
(restrict (some ()) ys).map (fun r => r.2.2) =
(restrict none xs).filterMap inputMBO ++ pendingMBO ys ∧
((exposedReplies (resourceRoute (U := U)) ys).map fun v => v.2.2).Pairwise (· ≤ ·) ∧
∀ b ∈ (exposedReplies (resourceRoute (U := U)) ys).map (fun v => v.2.2),
b ≤ latestMBO (restrict none xs) := by
-- The pair `[c, R]` driven by the attachment connection: the outside queries `u₁, u₂, …` enter
-- `c` one at a time; the inside queries of `c` go to `R` and the replies of `R` go back to `c`;
-- the outside replies of `c` leave (they are the exposed replies).
--
-- ┌───┐ ───── x ─────▶ ┌───┐
-- ── u₁ u₂ … ─────▶ │ c │ │ R │
-- ◀── (v, b₀) ───── └───┘ ◀─── (y, b) ── └───┘
--
-- `c` tags each outside reply with `b₀`, the MBO of the latest inside reply it received (`hc`).
-- The claim, along a transcript of the pair:
-- (i) the part of the transcript at `R` is a transcript of `R`;
-- (ii) the MBOs of the replies of `R` are those `c` has received, followed by the MBO of the
-- reply of `R` on its way to `c`, if `R` has just replied (`pendingMBO`);
-- (iii) the MBOs of the exposed replies are monotone;
-- (iv) each of them is ≤ the latest MBO `c` has received.
-- Plan, by induction on the transcript, one input `x` and one output `y` of `[c, R]` per round:
-- 1. the transcripts of `R` have monotone MBOs (`hR`);
-- 2. the connection feeds one of three inputs: (a) the next outside query, while no reply of
-- `R` is pending; (b) the pending reply of `R`, to `c`; (c) an inside query of `c`, to `R`;
-- 3. the latest MBO received by `c` does not decrease when the new MBO, if any, is ≥ all
-- those received before;
-- 4. case (a): an outside reply of `c` now carries the latest MBO, ≥ all exposed before;
-- 5. case (b): by (ii) and Step 1, the pending MBO is ≥ all received, and becomes the latest;
-- 6. case (c): `R` answers, and its MBO becomes pending.
induction tr with
-- The empty transcript: all four parts hold.
| nil => simp [pendingMBO, exposedReplies]
| @snoc xs ys x y tr hx hy ih =>
-- One more round: the input `x`, fed by the connection after the outputs `ys`, and the output
-- `y` of `[c, R]`; (i)–(iv) hold before it.
obtain ⟨hr, hbits, hmono, hle⟩ := ih
-- Step 1, for a transcript given by its queries and its replies.
-- The resource's transcripts have monotone MBOs.
have hRmono : ∀ {xs' ys'}, Replies R xs' ys' → (ys'.map fun r => r.2.2).Pairwise (· ≤ ·) := by
intro xs' ys' hr'
have hl : xs'.length = ys'.length := hr'.length.symm
-- Zip the queries and replies into one transcript and apply `hR`.
have := hR (xs'.zip ys') (by rwa [List.map_fst_zip (le_of_eq hl),
List.map_snd_zip (le_of_eq hl.symm)])
-- The MBOs of the zipped transcript are those of the replies.
have hm : ((xs'.zip ys').map fun z => z.2.2.2) = (ys'.map fun r => r.2.2) := by
rw [show (fun z : (Σ j, X j) × (Σ j, withMBO Y j) => z.2.2.2) =
(fun r : Σ j, withMBO Y j => r.2.2) ∘ Prod.snd from rfl, ← List.map_map,
List.map_snd_zip (le_of_eq hl.symm)]
rwa [hm] at this
-- Step 2.
-- What the connection feeds next.
have hfeed : (∃ u, x = resourceInput u ∧ pendingMBO ys = []) ∨
(∃ ys' : List (Two (Σ l, twoFam (withMBO V) X l) (Σ j, withMBO Y j)),
∃ (j : J) (r : withMBO Y j),
ys = ys' ++ [⟨some (), ⟨j, r⟩⟩] ∧ x = ⟨none, ⟨⟨some (), j⟩, r⟩⟩) ∨
(∃ ys' : List (Two (Σ l, twoFam (withMBO V) X l) (Σ j, withMBO Y j)),
∃ (j : J) (q : X j),
ys = ys' ++ [⟨none, ⟨⟨some (), j⟩, q⟩⟩] ∧ x = ⟨some (), ⟨j, q⟩⟩) := by
-- Case on the last output of the pair.
rcases List.eq_nil_or_concat ys with rfl | ⟨ys', y', rfl⟩
-- No output yet: the connection feeds the first outside query (there is one, since the
-- connection is defined), and no reply is pending.
· left
rw [connectionQueries_nil] at hx
cases us with
| nil => exact absurd hx (by simp)
| cons u us =>
refine ⟨u, ?_, rfl⟩
simpa [eq_comm] using hx
-- A last output `y'`: the connection routes it.
· rw [List.concat_eq_append] at hx ⊢
-- The connection was already defined after `ys'`.
have hd' := connectionQueries_prefix _ _ _ _ _ (Part.dom_iff_mem.mpr ⟨x, hx⟩)
-- `y'` is an output of `c` or a reply of `R`.
rcases y' with ⟨_ | ⟨⟩, y'⟩
-- An output of `c`: an outside reply or an inside query.
· rcases y' with ⟨⟨_ | ⟨⟨⟩⟩, o⟩, v⟩
-- An outside reply of `c` is exposed; the connection feeds the next outside query (a).
· left
have hx' := (connectionQueries_snoc_out resourceRoute resourceInput us ys'
(y := ⟨none, ⟨⟨none, o⟩, v⟩⟩) (b := ⟨o, v⟩) rfl) ▸ hx
simp only [Part.mem_ofOption, Option.mem_def, Option.map_eq_some_iff] at hx'
obtain ⟨u, -, rfl⟩ := hx'
exact ⟨u, rfl, by simp [pendingMBO]⟩
-- An inside query `q` of `c` at `j` is forwarded to `R` (c).
· right; right
have hx' := (connectionQueries_snoc_in resourceRoute resourceInput us ys'
(y := ⟨none, ⟨⟨some (), o⟩, v⟩⟩) (x := ⟨some (), ⟨o, v⟩⟩) rfl hd') ▸ hx
exact ⟨ys', o, v, rfl, Part.mem_some_iff.mp hx'⟩
-- A reply `r` of `R` at `j` is fed back to `c` as an inside reply (b).
· right; left
rcases y' with ⟨j, r⟩
have hx' := (connectionQueries_snoc_in resourceRoute resourceInput us ys'
(y := ⟨some (), ⟨j, r⟩⟩) (x := ⟨none, ⟨⟨some (), j⟩, r⟩⟩) rfl hd') ▸ hx
exact ⟨ys', j, r, rfl, Part.mem_some_iff.mp hx'⟩
-- Step 3, for a new input `cx` whose MBO, if any, is ≥ all MBOs received by `c`.
-- On such an input the latest inside MBO does not decrease.
have hlatest : ∀ cx, (∀ b, inputMBO cx = some b →
((restrict none xs).filterMap inputMBO ++ [b]).Pairwise (· ≤ ·)) →
latestMBO (restrict none xs) ≤ latestMBO (restrict none xs ++ [cx]) := by
intro cx hcx
-- The new latest MBO is that of `cx` if `cx` carries one, the old one otherwise.
rw [latestMBO_snoc]
cases hb : inputMBO cx with
| none => exact le_rfl
| some b =>
-- The old latest MBO is the last MBO received (or unset), ≤ `b` by monotonicity.
rw [latestMBO_eq]
exact getLast?_getD_le (hcx b hb)
-- Steps 4–6: the three cases of Step 2.
rcases hfeed with ⟨u, rfl, hpend⟩ | ⟨ys', j, r, rfl, rfl⟩ | ⟨ys', j, q, rfl, rfl⟩
· -- An outside query of the converter.
-- Step 4, case (a): `c` receives the outside query `u` and outputs `cy`.
simp only [resourceInput] at hy ⊢
rw [pair_left] at hy
obtain ⟨cy, hcy, rfl⟩ := (Part.mem_map_iff _).mp hy
-- `u` carries no MBO: the latest MBO received by `c` is unchanged (in particular ≤).
have hl := hlatest ⟨⟨none, u.1⟩, u.2⟩ (fun b hb => by simp [inputMBO] at hb)
-- The new input goes to `c`; the part at `R` is unchanged.
simp only [restrict_none_snoc_left, restrict_some_snoc_left, List.filterMap_append,
inputMBO, List.filterMap_cons, List.filterMap_nil, List.append_nil] at hl ⊢
-- (i) The part at `R` is unchanged. (ii) No MBO was pending, none is, and `c` received none.
-- (iii) and (iv) depend on `cy`.
refine ⟨hr, by rw [hbits, hpend]; simp [pendingMBO], ?_⟩
-- `cy` is an outside reply or an inside query.
rcases cy with ⟨⟨_ | ⟨⟨⟩⟩, o⟩, v⟩
-- An outside reply `(v, b₀)`: it is exposed, and `b₀` is the latest MBO received (`hc`).
· have hv := hc _ o v hcy
-- The exposed MBOs gain `b₀` at the end.
rw [exposedReplies_snoc_out resourceRoute _ (y := ⟨none, ⟨⟨none, o⟩, v⟩⟩) (b := ⟨o, v⟩) rfl,
List.map_append, List.map_singleton]
-- (iii) needs each earlier exposed MBO ≤ `b₀`; (iv) needs every exposed MBO ≤ `b₀`.
refine ⟨List.pairwise_append.mpr ⟨hmono, List.pairwise_singleton _ _,
fun a ha b hb => ?_⟩, fun b hb => ?_⟩
-- An earlier exposed MBO ≤ the old latest MBO ≤ the new one, which is `b₀`.
· rw [List.mem_singleton.mp hb, hv]
exact (hle a ha).trans hl
-- An earlier exposed MBO as above; `b₀` itself.
· rcases List.mem_append.mp hb with hb | hb
· exact (hle b hb).trans hl
· rw [List.mem_singleton.mp hb, hv]
-- An inside query: it goes to `R` and nothing new is exposed; (iv) as the latest MBO did not
-- decrease.
· rw [exposedReplies_snoc_in resourceRoute _ (y := ⟨none, ⟨⟨some (), o⟩, v⟩⟩)
(x := ⟨some (), ⟨o, v⟩⟩) rfl]
exact ⟨hmono, fun b hb => (hle b hb).trans hl⟩
· -- A reply of the resource returned to the converter.
-- Step 5, case (b): `c` receives the pending reply `r` of `R` at `j`, with MBO `r.2`, and
-- outputs `cy`.
rw [pair_left] at hy
obtain ⟨cy, hcy, rfl⟩ := (Part.mem_map_iff _).mp hy
-- `r.2` was pending.
have hpend :
pendingMBO (ys' ++ [(⟨some (), ⟨j, r⟩⟩ : Two _ (Σ j, withMBO Y j))]) = [r.2] := by
simp [pendingMBO]
-- By (ii) and Step 1, the MBOs received by `c`, followed by `r.2`, are monotone.
have hRm := hRmono hr
rw [hbits, hpend] at hRm
-- So the latest MBO received by `c` does not decrease (Step 3), and it is now `r.2`.
have hl := hlatest ⟨⟨some (), j⟩, r⟩ (fun b hb => by
simp only [inputMBO, Option.some.injEq] at hb
rwa [← hb])
have hlast : latestMBO (restrict none xs ++ [⟨⟨some (), j⟩, r⟩]) = r.2 := by
rw [latestMBO_snoc]; rfl
-- The new input goes to `c`; the part at `R` is unchanged.
simp only [restrict_none_snoc_left, restrict_some_snoc_left] at hl hlast ⊢
-- (i) The part at `R` is unchanged. (ii) `r.2` moves from pending to received.
refine ⟨by simpa using hr, by
rw [hbits, hpend]
simp [pendingMBO, List.filterMap_append, inputMBO], ?_⟩
rcases cy with ⟨⟨_ | ⟨⟨⟩⟩, o⟩, v⟩
-- An outside reply `(v, b₀)`: by `hc`, `b₀` is the new latest MBO `r.2`.
· have hv := hc _ o v hcy
rw [hlast] at hv
-- The exposed MBOs gain `b₀` at the end.
rw [exposedReplies_snoc_out resourceRoute _ (y := ⟨none, ⟨⟨none, o⟩, v⟩⟩) (b := ⟨o, v⟩) rfl,
List.map_append, List.map_singleton]
-- (iii) An earlier exposed MBO ≤ the old latest ≤ `r.2` = `b₀`. (iv) The same, and `b₀`.
refine ⟨List.pairwise_append.mpr ⟨hmono, List.pairwise_singleton _ _,
fun a ha b hb => ?_⟩, fun b hb => ?_⟩
· rw [List.mem_singleton.mp hb, hv, ← hlast]
exact (hle a ha).trans hl
· rw [hlast]
rcases List.mem_append.mp hb with hb | hb
· rw [← hlast]; exact (hle b hb).trans hl
· rw [List.mem_singleton.mp hb, hv]
-- An inside query: nothing new is exposed; (iv) as the latest MBO did not decrease.
· rw [exposedReplies_snoc_in resourceRoute _ (y := ⟨none, ⟨⟨some (), o⟩, v⟩⟩)
(x := ⟨some (), ⟨o, v⟩⟩) rfl]
exact ⟨hmono, fun b hb => (hle b hb).trans hl⟩
· -- An inside query of the converter forwarded to the resource.
-- Step 6, case (c): `R` receives the inside query `q` at `j` and replies `r'`.
rw [pair_right] at hy
obtain ⟨r', hr', rfl⟩ := (Part.mem_map_iff _).mp hy
-- Nothing was pending: the last output was the query of `c`.
have hpend :
pendingMBO (ys' ++ [(⟨none, ⟨⟨some (), j⟩, q⟩⟩ : Two _ (Σ j, withMBO Y j))]) = [] := by
simp [pendingMBO]
-- The new input goes to `R`; the part at `c` is unchanged.
simp only [restrict_none_snoc_right, restrict_some_snoc_right]
-- (i) The part at `R` gains the round `(q, r')`.
refine ⟨replies_snoc.mpr ⟨hr, Part.eq_some_iff.mpr hr'⟩, ?_, ?_⟩
-- (ii) `r'.2` is now pending.
· rw [List.map_append, hbits, hpend]
simp [pendingMBO]
-- (iii), (iv): nothing new is exposed, and the history of `c` is unchanged.
· rw [exposedReplies_snoc_in resourceRoute _ (y := ⟨some (), r'⟩)
(x := ⟨none, ⟨⟨some (), r'.1⟩, r'.2⟩⟩) rfl]
exact ⟨hmono, hle⟩
/-- **Attaching a converter that tags its outside replies with the latest inside MBO** to a
resource with monotone MBOs gives transcripts with monotone MBOs. -/
lemma replies_apply_monotone {c : InsideOutsideSystem O J U (withMBO V) X (withMBO Y)}
(hc : ∀ h (o : O) (v : withMBO V o), ⟨⟨none, o⟩, v⟩ ∈ c h → v.2 = latestMBO h)
{R : InterfaceSystem J X (withMBO Y)}
(hR : ∀ t : List ((Σ j, X j) × Σ j, withMBO Y j), Replies R (t.map Prod.fst) (t.map Prod.snd) →
(t.map fun z => z.2.2.2).Pairwise (· ≤ ·))
{us vs} (hr : Replies (apply c R) us vs) : (vs.map fun v => v.2.2).Pairwise (· ≤ ·) := by
-- A transcript of `apply c R` is the exposed part of a transcript of `[c, R]` driven by the
-- connection (picture at `attach_tags_invariant`); part (iii) of that invariant is the claim.
rw [apply_eq] at hr
obtain ⟨xs, ys, tr, rfl, -⟩ := (replies_connection_iff us vs).mp hr
exact (attach_tags_invariant hc hR tr).2.2.1
/-- A lift tags its outside replies with the latest inside MBO. -/
lemma liftMBO_tag (a : InsideOutsideSystem O J U V X Y) (h) (o : O) (v : withMBO V o)
(hv : ⟨⟨none, o⟩, v⟩ ∈ liftMBO a h) : v.2 = latestMBO h := by
-- In the picture at `liftMBO`, the MBO wire tags the outside reply with `latestMBO h`.
have he := (mem_liftMBO.mp hv).2
simp only [outsideMap, forgetMBO, tagMBO, Sigma.mk.inj_iff, heq_eq_eq, true_and] at he
exact congrArg Prod.snd he
variable [Fintype O] [Fintype J] [∀ o, Fintype (U o)] [∀ o, Fintype (V o)]
[∀ j, Fintype (X j)] [∀ j, Fintype (Y j)]
{E : List (Σ j, X j) → Prop} {m : ℕ} {hE : ∀ h, E h → h.length ≤ m}
{F : List (Σ o, U o) → Prop} {n : ℕ} {hF : ∀ h, F h → h.length ≤ n}
/-- **Attaching a lift to a game gives a game**: the outside MBO is the MBO of the latest
inside reply, which, once set, stays set. -/
theorem RandomSystem.MonotoneMBO.attach_liftMBO
(hE' : ¬ E [] ∧ ∀ {p h}, p <+: h → p ≠ [] → E h → E p)
(hF' : ¬ F [] ∧ ∀ {p h}, p <+: h → p ≠ [] → F h → F p)
(α : PDCBehavior O J U V X Y E m hE F n hF)
{G : RandomSystem (Σ j, X j) (Σ j, withMBO Y j) (Domain.ofInputs E m hE)}
(hG : G.RepliesAtQueriedInterface) (hm : G.MonotoneMBO) :
(PDCBehavior.attach hE' hF' (PDCBehavior.liftMBO α) G hG).MonotoneMBO := by
-- A transcript `k` of positive mass comes from a sampled lifted DDC `liftMBO a` attached to a
-- sampled DDS `s` of the game:
--
-- ┌───────────┐ ──── x ────▶ ┌───┐
-- ─u─▶ │ liftMBO a │ │ s │ the MBOs `b` of `s` are monotone, as those of `G`
-- ◀─── └───────────┘ ◀── (y, b) ─ └───┘
-- (v, b₀) `b₀` is the latest `b`, so it is monotone too (`replies_apply_monotone`)
--
-- Plan:
-- 1. present `α` by DDCs (`P`) and `G` by DDSs (`Q`);
-- 2. write the mass of `k` through the lifted presentation and `Q`;
-- 3. some pair `(liftMBO a, s)` of positive weight has the transcript `k`;
-- 4. the transcripts of `s` have positive mass under `G`, hence monotone MBOs;
-- 5. apply `replies_apply_monotone`, the lift tagging with the latest MBO (`liftMBO_tag`).
-- Step 1.
obtain ⟨b, P, hP⟩ := α.2
obtain ⟨Q, hQ⟩ := RandomSystem.exists_resource_presentation hE' G hG
-- Step 2.
intro k hk
rw [PDCBehavior.attach_presents hE' hF' _ G hG (liftPresentation P) Q
(PDCBehavior.liftMBO_presents α P hP) hQ k, behaviorMass_attachPDS] at hk
-- Step 3 (otherwise the mass of `k` is 0).
obtain ⟨st, hst, hrep⟩ : ∃ st ∈ (Distribution.prod (liftPresentation P).1 Q.1).support,
Replies (trim (apply st.1.1 st.2.1)) (k.map Prod.fst) (k.map Prod.snd) := by
by_contra hn
push Not at hn
apply hk
rw [Distribution.mass_congr_of_support _ (Q := fun _ => False)
(fun a ha => iff_false_intro (hn a ha))]