From 2aa8ee16ea87cdfc39c1d1f1a3c56b05c46e107e Mon Sep 17 00:00:00 2001 From: KiHoLee Date: Mon, 17 Aug 2026 15:26:34 +0900 Subject: [PATCH] Task-level retrieval experiment (Fig 5), benchmark ban, page fit --- README.md | 7 +- code/replot_all.py | 31 ++++++++- code/retrieval_real.py | 147 ++++++++++++++++++++++++++++++++++++++++ data/retrieval_real.csv | 14 ++++ fig/fig_retrieval.pdf | Bin 0 -> 27626 bytes 5 files changed, 195 insertions(+), 4 deletions(-) create mode 100644 code/retrieval_real.py create mode 100644 data/retrieval_real.csv create mode 100644 fig/fig_retrieval.pdf diff --git a/README.md b/README.md index 67bedf3..0ff192c 100644 --- a/README.md +++ b/README.md @@ -48,9 +48,10 @@ Run the scripts from inside `code/`. All plots are rendered from | Fig. 2 | Per-user MSE, aware vs blind floor | `revision_sims.py E1` | `floor_validation.csv` | | Fig. 3 | Effective sum rate at the CLIP affinity | `revision_sims.py E7a` | `rate_corrected.csv` | | Fig. 4 | Cosine recovery on real BERT+ViT pairs | `fig_real_merged.py`, then `refine_matched.py` | `bertvit_merged.csv` | -| Fig. 5 | Receiver comparison under Rayleigh fading | `revision_sims_gpu.py E2` | `sic_comparison.csv` | -| Fig. 6 | Value of the measured affinity | `revision_sims.py E7a` | `beta_sweep_corrected.csv` | -| Fig. 7 | Multi-user scaling (joint Wiener) | `revision_sims_gpu.py E7c` | `multiuser_corrected.csv` | +| Fig. 5 | Top-1 retrieval with recovered embeddings | `retrieval_real.py` | `retrieval_real.csv` | +| Fig. 6 | Receiver comparison under Rayleigh fading | `revision_sims_gpu.py E2` | `sic_comparison.csv` | +| Fig. 7 | Value of the measured affinity | `revision_sims.py E7a` | `beta_sweep_corrected.csv` | +| Fig. 8 | Multi-user scaling (joint Wiener) | `revision_sims_gpu.py E7c` | `multiuser_corrected.csv` | Quantities quoted in the text but not plotted come from the same drivers: `revision_sims.py E0` writes `theorem_check.csv` (Theorem 1 diff --git a/code/replot_all.py b/code/replot_all.py index ceb5f33..ad624db 100644 --- a/code/replot_all.py +++ b/code/replot_all.py @@ -179,11 +179,40 @@ def fig_multiuser(): save(fig, "fig_multiuser_corrected") +# ------------------------------------------------ fig_retrieval +def fig_retrieval(): + rows = rows_of("retrieval_real") + snr = col(rows, "snr_db") + fig, ax = plt.subplots() + ax.plot(snr, col(rows, "edma"), "o-", color="C3", label=LBL["edma"]) + ax.plot(snr, col(rows, "hybrid"), "^-", color="C2", + label=LBL["hybrid"]) + ax.plot(snr, col(rows, "todma"), "d-.", color="C4", + label=LBL["todma"]) + ax.plot(snr, col(rows, "oma"), "v:", color="C1", label=LBL["oma"]) + ax.plot(snr, col(rows, "genie"), "-", color="gray", lw=1.0, + label=LBL["genie"]) + ax.axhline(1.0 / 16, color="gray", ls=":", lw=0.8) + ax.annotate("chance $1/16$", xy=(10.5, 1.0 / 16 + 0.015), fontsize=7, + color="gray") + ax.set_xlabel("SNR $\\rho$ [dB]") + ax.set_ylabel("Top-1 retrieval accuracy") + ax.set_xlim(snr[0], snr[-1]); ax.set_ylim(0, 1.42) + ax.set_yticks([0.0, 0.2, 0.4, 0.6, 0.8, 1.0]) + hs, ls = ax.get_legend_handles_labels() + order = [1, 4, 0, 2, 3] # long labels share column one + ax.legend([hs[i] for i in order], [ls[i] for i in order], + loc="upper center", ncol=2, columnspacing=0.7, + handlelength=1.3, handletextpad=0.5) + save(fig, "fig_retrieval") + + if __name__ == "__main__": import sys todo = set(sys.argv[1:]) ALL = {"floor": fig_floor, "rate": fig_rate, "beta": fig_beta_sweep, - "sic": fig_sic, "multi": fig_multiuser} + "sic": fig_sic, "multi": fig_multiuser, + "retr": fig_retrieval} for name, fn in ALL.items(): if not todo or name in todo: fn() diff --git a/code/retrieval_real.py b/code/retrieval_real.py new file mode 100644 index 0000000..3701b1d --- /dev/null +++ b/code/retrieval_real.py @@ -0,0 +1,147 @@ +""" +Task-level validation: nearest-neighbor retrieval with recovered +embeddings on the real BERT/ViT pairs. +=================================================================== +For every recovered embedding, rank the 16 clean database embeddings +of the SAME modality by absolute cosine and score top-1 retrieval of +the transmitted item (chance level 1/16). This measures whether the +recovery preserves semantic identity, the operational question behind +the cosine metric of the manuscript. + +Schemes: EDMA (aware Wiener), hybrid (EDMA + stored refinement gate +from data/refine_gates.npz), OMA (equivalent-bandwidth model), +genie-aided SIC bound, ToDMA-adapted (first 40 fading draws). + +Same channel, energy, and seed conventions as fig_real_merged.py. +Writes data/retrieval_real.csv. Run under WSL (torch, CUDA). +""" +from __future__ import annotations +import csv +import math +import time +import numpy as np +import torch + +from fig_real_merged import (load_pairs, haar_t, aware_batch, todma_prepare, + omp_code, todma_run, SNRS, NFADE, NFADE_TOD, + D, DATA, DEV, SEED) + +torch.manual_seed(SEED) +rng = np.random.default_rng(SEED) + + +def refine_apply_single(P1, z): + """z: (b, D) real torch tensor; P1: (D, D) gate.""" + return D * torch.softmax((z @ P1.T) / math.sqrt(D), dim=1) * z + + +def top1(rec, db, idx): + """rec: (b, D) cfloat; db: (n, D) float; returns (b,) 0/1 hits.""" + sims = (rec @ db.T.to(rec.dtype).conj()).abs() # (b, n) + sims = sims / (rec.norm(dim=1, keepdim=True) + * db.norm(dim=1).unsqueeze(0)) + return (sims.argmax(dim=1) == idx).float().cpu().numpy() + + +def main(): + A, B, betas = load_pairs() + npairs = len(A) + gates = np.load(DATA / "refine_gates.npz") + P1 = torch.tensor(gates["P1"], dtype=torch.float32, device=DEV) + tod = todma_prepare() + codes = [(omp_code(tod[0], A[i], tod[3]), + omp_code(tod[0], B[i], tod[3])) for i in range(npairs)] + At = torch.tensor(A, dtype=torch.float32, device=DEV) + Bt = torch.tensor(B, dtype=torch.float32, device=DEV) + gen = torch.Generator(device=DEV).manual_seed(SEED) + nb = len(SNRS) + sigs = torch.tensor(10 ** (-SNRS / 20.0), dtype=torch.float32, + device=DEV) + keys = ("edma", "hybrid", "oma", "genie", "todma") + acc = {k: np.zeros(nb) for k in keys} + cnt = {k: np.zeros(nb) for k in keys} + t0 = time.time() + for i in range(npairs): + bi = float(betas[i]) + e1, e2 = At[i], Bt[i] + c1c, c2c = codes[i] + for f in range(NFADE): + M = haar_t(2, gen) + M1, M2 = M[0], M[1] + Q = M1.T @ M2 + h = (torch.randn(2, generator=gen, device=DEV) + + 1j * torch.randn(2, generator=gen, device=DEV)) \ + / math.sqrt(2) + n = (torch.randn(D, generator=gen, device=DEV) + + 1j * torch.randn(D, generator=gen, device=DEV)) \ + / math.sqrt(2) + n2 = (torch.randn(D, generator=gen, device=DEV) + + 1j * torch.randn(D, generator=gen, device=DEV)) \ + / math.sqrt(2) + r0 = h[0] * (M1 @ e1).to(torch.cfloat) \ + + h[1] * (M2 @ e2).to(torch.cfloat) + r = r0.unsqueeze(0) + sigs.view(-1, 1) * n.unsqueeze(0) + t1 = (M1.T.to(torch.cfloat) @ r.unsqueeze(-1)).squeeze(-1) / h[0] + t2 = (M2.T.to(torch.cfloat) @ r.unsqueeze(-1)).squeeze(-1) / h[1] + c1 = (h[1] / h[0]).item() + c2 = (h[0] / h[1]).item() + v1 = sigs**2 / h[0].abs()**2 + v2 = sigs**2 / h[1].abs()**2 + g1 = aware_batch(t1, Q, bi, c1, v1) + g2 = aware_batch(t2, Q.T, bi, c2, v2) + acc["edma"] += 0.5 * (top1(g1, At, i) + top1(g2, Bt, i)) + hy1 = refine_apply_single(P1, g1.real.float()).to(torch.cfloat) + hy2 = refine_apply_single(P1, g2.real.float()).to(torch.cfloat) + acc["hybrid"] += 0.5 * (top1(hy1, At, i) + top1(hy2, Bt, i)) + o1 = e1.to(torch.cfloat).unsqueeze(0) \ + + math.sqrt(2) * sigs.view(-1, 1) * n.unsqueeze(0) / h[0] + o2 = e2.to(torch.cfloat).unsqueeze(0) \ + + math.sqrt(2) * sigs.view(-1, 1) * n2.unsqueeze(0) / h[1] + acc["oma"] += 0.5 * (top1(o1, At, i) + top1(o2, Bt, i)) + ge1 = (M1.T.to(torch.cfloat) + @ (r - h[1] * (M2 @ e2).to(torch.cfloat)).unsqueeze(-1) + ).squeeze(-1) / h[0] + ge2 = (M2.T.to(torch.cfloat) + @ (r - h[0] * (M1 @ e1).to(torch.cfloat)).unsqueeze(-1) + ).squeeze(-1) / h[1] + acc["genie"] += 0.5 * (top1(ge1, At, i) + top1(ge2, Bt, i)) + for kk in ("edma", "hybrid", "oma", "genie"): + cnt[kk] += 1 + if f < NFADE_TOD: + hnp = (complex(h[0].item()), complex(h[1].item())) + nslots = [(rng.standard_normal(tod[4]) + + 1j * rng.standard_normal(tod[4])) + / math.sqrt(2) for _ in range(tod[3])] + for k, s in enumerate(SNRS): + sig = 10 ** (-s / 20.0) + recs = todma_run(tod, (c1c, c2c), hnp, sig, nslots) + hit = 0.0 + for j, (rec, db, ii) in enumerate( + ((recs[0], A, i), (recs[1], B, i))): + if rec is None: + continue # failed detection: no hit + sims = np.abs(db @ rec) / ( + np.linalg.norm(db, axis=1) + * np.linalg.norm(rec)) + hit += 0.5 * float(int(np.argmax(sims)) == ii) + acc["todma"][k] += hit + cnt["todma"][k] += 1 + print(f" pair {i+1}/{npairs} done ({time.time()-t0:.0f}s)", + flush=True) + for k in keys: + acc[k] /= np.maximum(cnt[k], 1) + + with open(DATA / "retrieval_real.csv", "w", newline="") as f: + w = csv.writer(f) + w.writerow(["snr_db"] + list(keys)) + for k, s in enumerate(SNRS): + w.writerow([s] + [acc[key][k] for key in keys]) + print(f"[OK] wrote {DATA/'retrieval_real.csv'}") + for k, s in enumerate(SNRS): + print(f" {s:4.1f} dB EDMA {acc['edma'][k]:.3f} " + f"hybrid {acc['hybrid'][k]:.3f} ToDMA {acc['todma'][k]:.3f} " + f" OMA {acc['oma'][k]:.3f} genie {acc['genie'][k]:.3f}") + + +if __name__ == "__main__": + main() diff --git a/data/retrieval_real.csv b/data/retrieval_real.csv new file mode 100644 index 0000000..3f1093b --- /dev/null +++ b/data/retrieval_real.csv @@ -0,0 +1,14 @@ +snr_db,edma,hybrid,oma,genie,todma +0.0,0.1915625,0.2253125,0.1278125,0.190625,0.028125 +2.5,0.278125,0.325,0.1884375,0.2840625,0.03671875 +5.0,0.4065625,0.45375,0.2784375,0.4103125,0.05390625 +7.5,0.5365625,0.5765625,0.3965625,0.54,0.0765625 +10.0,0.6625,0.6940625,0.52375,0.6675,0.1390625 +12.5,0.7646875,0.781875,0.6515625,0.76375,0.23359375 +15.0,0.8296875,0.84875,0.7515625,0.833125,0.371875 +17.5,0.8828125,0.8934375,0.828125,0.89,0.515625 +20.0,0.9178125,0.926875,0.881875,0.925625,0.65859375 +22.5,0.94625,0.9503125,0.9175,0.9565625,0.7703125 +25.0,0.96125,0.9625,0.94625,0.9734375,0.84609375 +27.5,0.970625,0.971875,0.9690625,0.983125,0.8921875 +30.0,0.9796875,0.98,0.9809375,0.99125,0.91640625 diff --git a/fig/fig_retrieval.pdf b/fig/fig_retrieval.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ca4f5dabebed58be5fecf6164c60a3cc309f8a7d GIT binary patch literal 27626 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ৄ_qlzlYQUM6w%BEtp#j2S9KgVgGOEO z-B)sZq~L*kTHgD}k!cO``34p`Cm?mgpAlr{5edw#W|k*22&~!=31%Zghy0=dP8^=! z3KJyJrL)9WIP>K^T>=5$#YZY!RhiiuBxa`N$tiJe9mjV7hAm>+JTN~n2=j5jmdg=j1(V*% z`Pn%>NLfLf$_Z>Cv~6AAqcWFfU5hfY3?)qYTg$k@kByh4l~>(ImiV`hOv#b?X%pCV z@!~`(>CCJAtArW$+%s|+NxJ1KT4guNSei&d2ZAr_XVmZ-I46Ul>D?9@254p7XymFV zlHfP_V?iv^hpVgJs&?*BZL9l6Eu4&oE3)x#YDo|F=0yn~Y(tJ&HiJ=!k+BV1G_KlUVv9~K8{CTb=4{ryk z=S%FN9;D8wpA3zLtev{829)W~stuA4FzR}3oO%RxK1~?Kg^j0?x;Eur@NQDM4)^!> zgU4;UR>>SEtnBaX2o7jEou7g(a&XY0->V%aXXPs8(?_mJ%Ui?f{5lMn`zULvKuNAF{Q za5u~KyDRK5cctixVUmZ`-AZ9l-4!*$s*6RiIa5(HQ(F`@@UA9SDqNZ3ZlOUmy|aJ$*awqmIqmW&*&B|y_ti83<2DkmdJCb~Fur>f9R zN7LEPORg?Y^WoC-$l{3ms9#gz#LugZ62z_*b_$Q9sXGL`9M;9JDOv3t{*BuhvClhj zL@)Fjg33)7yuVylY2e&=UxyA{t-H4o1MH2}dxQo$6}hn%@NP(L7>_7WgFYZ&Dj6Y! zNRJ$QfYzmJMRQM(yXG=js?({cd2UXtICU1XV5?wB&12pDsGK8t^b#J?yFKDFi~TGP zG#1XZeCj}389Na;z412h{E%5p#lB>eff~Vq%7G(0fTFjx)BGADv}VwYZC5EoD}ob$ z>x-%i9Hzl?xS+xJZE!!B-zZvZbeBqSdZccVU+42z4W>@$Omn-(E>E6R_3XfJ8LKhE ziu;r*e5@*|bzF^HShN5u@!8-b4dbNhxl{s|2(!*7eob}_uGqQtWxvjPT@2MRZZ;e$ zg2o+?^r_<9YGs!o;fXLJUf2(~x8>PnvpIwdwMDa*g!` zQELI~1h!?9&&h>=&wC`fFx2DnHO1Wy+zxHb7Tkbq;ftHVO!Hr#`)KsVYJbi?dA&F* zobf$e`_^+{Oc-F@(ag@yB%RxL0$)XvUmH{W`L6F;rm}TXPcqrX=&$KA?4<{PDVBc_ z{)~Y!k(99q?2PD>Hunq}jKi0J%$y}X(rjlEd4Gr7kE^`6i>eh`_jUv+b?s*|P4$ct z0O55Lc6FI#ktWHcniH(185BjbSF%?tBQwuG-MDuwh?MxphdqNF=}Q=5AC$T5E*93? zn%JAp{5o#>Gd5Amby{XBbjV1gp7v5NsyO)C_kJW6Om@87wdi*~Zcwbvf7nevx2LD^ zC$AWKF(L8f;(F8)OTM!Ig6pbnoWnuXD^g~8`R#jIjbx*m36MM$dS*wC{ea`E4otYb z&`}NdoryJE*Vm=G~9yxs=DH zAh}Eq4!#GC@t(wyhKpB`$J1dfH@@rjBAHD-5w|8n{|1~Kgrb&Fi(D>6gB(Cf`{8Bv{`C!LQ#GbPK5~Ja zbYC$V80&O}6uYZ>qBv&l#ca_28mU<(@w5s<1=n9@W!<1r<>SAge`J4dKOA3goAK&( zTUZ-_9tT#^R2$%B8JNgBUPPtaVNA4*FR+`XaO3A`;ChiV|J*`)zJ8Hdu5o)yQBgjm zv9FPwfv^ZpJ4P=Ejbn+8aYYf?;8mh}nqe+cJG*8sk|gvcN%+o8RzUAzMP+Z9SA>#WvtDXcm@~Yc* zM>7(UNYTRZ_H(|oM831Yr$DE2#rfxP9T0xLhDf_>5!2u7&E6mUQ^=5M zW3H|kDpV-yx{P2^!$Pfbcv(UU#i*LKcmff9BtM^~a>EZyZ?7gA2UY1Zxot@t6c2r? z5P={X)v6%(VWrbJGHoFTMU+(mWv`$z_0gE;3>coaCP~UsP>ywB`(x=Mp z%44#l_DsW9E~d1u7}7OC#t|J5Zz-l#&r#A!E&|}nL!N=@q~VS@tyCcmpN=8nh`n%Y zjUL2nJ#+8kN|INInPcEDa1vUw2aS2|+QUBim@A?=o4cDUw9VzYNd5>c2n-(nfKNpe zW>mt)7JTzQzY13p8D1p`G;(qgkvz+V#I83a{VqE3G575Lh4^B!Ip#}1Qe#=ldD7GI z2DKMd=mql6$EfZ_1-y_8q>)ncBXJOsG<;$h))MbSxV9D0BDSAgLhvT?B_+l4?p!-) zd2y6Vj#@x&V43Dn+obF~|7eTIM;0o3b4|ZuJJzW2@sRY?EF*0ju5=ak%Wx-n`|PA@ zsiLQ%N64GTtGfPt*%!X&$s9UHB0!*(e+Im)hHf@K1B;@B<$@QkvLlSul2@1kM6yPE z$|<``)RvHVs5Bk~ct*6W>6q)8Z9?} zdcf+N5wGB^6+hsKR(KvW5pzvIkgXg#r$pNpVVhxn;sn_y)B36IG{iZ=43C2uu|oS+ z4DU97!mNQT?6MJE+h4nrZZ_u_+or8o&4$4(3T<$Oe05-TkjE^7#n#!T{zpAQ^3F%? zs;U{iV-4c!h9a0x09bCTMXnvu&1qAnDv#$3` zB|P7aG_V>c-DxpWk5g9g@K4vC*Fx7y)+|krRCxF-Vs-!dc?i zFS1Yg+?0FNASr;we*WS2>i)6i)UVqYZ)2mIHDzEzoKBjY-Spitt%}u!S87R>39k91 zsEIKPxJ$g6rWO2h--@ILSf{NH;>k6+YDmm_2&ieG0dMQoTMY@-28zG|yyJ|F4Wdg< z;DKhEB-q681ot8LAwNZ%6j*q9K&2rOzjsPyk+uJD`b6~!6<n$oA9G14)YP};OK(~)u7rgYcNP0_&Kz?Pg)w+aUs2Sxmb zfk087>*$dGdFrcwhu=%hVzqu(BM)e$ev*j5|SX{vepSJ9NZEJw^=7k{j`H&wr^R85f8xdSUnJ&3nSp54#}n~km& zV~DCg@;?R^-Hv3tUi;lUUcA14M|QogHZk0?qrO%DSaqjLZLT@Hmj{Xi<0lE*+U`ru zD&M?Lj(c&&>FBg>uZ;GX-!R%DWOtCFZ;G5O^@c5&$x9rh2!~CW4KR8LkRfujPZxT(AkKf7A<&*m6;~w%?7ltQ0nGTO`IeToel4_E%6z8xB~&fi16h)E z165=FjCwmJY2EK>rVJMZ9Mp!C$(SG|k=r6Y_VJcb@D3gEYrsTwqapjzV-d}KEhmW! zM<&UHNhlRROt+r~n>SCN0E=T<$D~i6nz^s`m^3!>En@UIv7o2ETZ%nPd4TO}JGtpv z8Fl9~ES0)ejgS3I`2+ht!JAqb^D57DPVat!#l=tTo*BZ5m*SmLhBfSkR+c;`x#ule7=6Lnz5GkwO`N9 z>YKv{_>DDH=a^O4uJ{&}-8$xi1~0}X6rgo-6M^(%gMhT|;+#b_t{WhU+;rZo>dc=z z(*DdpBkMqZ)~z$U0q)1dU9;Qb@x$zglJx^-WpP-0bOg8YTDrc}j66*EjZUL##flfp z*QQLu_2zGer(a)2UJa|}L*E)sQMYq6y7mk$ng>P)CvPBX3 z=HHgo#ArA0^IjO49hg(ki;$!~8h-*u^k5=SIybFL{ODOi?^p7fb;gTTYs6LCCfv?A zFMHH+q@5^Gb>0?|=aUtE#^!QfdUaujo#WP!77?+jSjG(y!d4!-#H?7}o_KmFeBLEH zRBHdrVdhHLb9Kk@qtFPw@?&306_1RhHRr#xQyK>B9C+Nty^xwfhLt1f?~$u!Gr$;b4Wmp{O zBk2A~imU$HW$Y$zGj}mds_|MT{z%v^8HLz)dFK&%CA)8n1xmCW_S-gG^5IWXh_KHs z2%6N-tvp`R(%i3szQlY!3HDVr<0Yn}Og$AsbP=|BsT-6LaG!!6KCzoNjSO`8-pkKk z2kDW+#qfPKU5z1FZRAw~x&nAZ(RI7zp~gQrrvo>;^gU11yWV!L1+tw)Hs;1PBZx;Av|2-Sa z%|_?BxlhRbvc91LsUaS$C=_0mp3rZ1Jh8^wtqlD-^-;MH01YP$$yJNmHa984GM$#x zGF!b$lab-MIT1{E=k!V;cJ(V>U+{Hw*4OncY^!G=0Aa?JUP80ZK3o!ANxI_Kz<5p8N1Dis!I36TX!By3KFB~PvX_Wih}&b>v2 zSuu-M%CD2e4fl$2&MZs6-LRd7Uq>DRH0G&>!Dzd}^yN)#+;@`2;&7z~6HJdk7Vx={ zvFONE;l?lE?_6@;01k!AVs=K@C5G6c&DbG#+L)8`g%lA#z1_nAE`E+V8c47KtkWZe6|rTsLqifk#O3=vP6W#&JXLGg2A_B z-}n?PB=Q|Z1U@_y5^?#>=_0u|RH{g8J@^|XD{@isJcTZ8yY|mN z->*=0Em5i1Y!_Q~$A1%(T?OiE^`~0Vgf0{FWXK8Ep9t@kt!kFLYuDTP)hl(#ZCs_Iuo zITC7hqh|JV@Ig6ktCueiAB-zJ<1yRLxqywWW!iY@cxi|Jp4VnQF(mW>bHqebPNP0yjODa*W&|$G!%b)>oo4 z&P}Q^{hZd;Pa|y?(z-KkEB7T+1|Lx4Im4w-cIs3j5_Ob)I(dSTKR6QO?ub;*;Y7f- z2w%#(;swE;5fjQ&wv|zS$jB!JwajJ5?VkKk47e=@(6CW;y$0k zTgBbw?1GzW-Cz3F?sIai3b$+Rug`xAR3NPK$~acI^!}yiU%tt)%DBq8%DT$SW7=rm z=qU6WpYc>$Lsry3yJIV5dr2V4wZ*l?7w6TTiaw&3={w{& z52MR*c?P4NgSXTAW$}W+hUjTMV&Al0d=fh@^SS^smS|}B$!vS3Oy#1Thla!7`bTxD zlWGiNv`(>~8WI-f=L8)n$pbU!8j*tTAC5_bkqNR+H)9I%L=3K}rLfmRNit;FJ#JFd zoxf0SM`y-3P--xEFT*Y>aI$4hxJ!=aOv7+me+4&u82|8$3UBJBZy1RG66Yf0m#Fm6 zUq9I?Z$<+R6+_6Auv)p=XuKJF6mG?==h@KOWYepAKel{%w2)iDiS@2jA$zYV z6w0i`p{!$-kRn7ziDbq9IjT3&`+48r|Nr^@j}Pv1J=eYO=YGz0o$vL%`EEZOGa@<4 z|3ZGjaMkT(duMWD(Xq@s;#*tzQ2ApY8y>yyzD;$m+E&oLb+A#rsqtHjV7qmEyH~JT z$ENv{N{vdXp4rtDejB$ekRhLq+e0;-2Wn6?IyGX>Yv9>iA272l`kj6m+@A>jN)zsn ztzE%t_s!NVK|W2lU70yTpm#y{!|@EMbFV9}&~a_Z#-UWx1qDvZW~ddZPg135e}w0A zMDAqH3?`Rwr%Xcr1Ls!{CqB|x~x3tyn z?aC&fZj?(_@qI)7x^X||abhZz8>%fKvhGy(2cnbjJ%vU({Vu!5$OXkMa4DwuTVUs8 z2G&25*YA=EdWM#QiPwSLrdbr#chwyR-2Lw2|DqZglqLC*@aK z=7~WC_65F))+t^oRmP@^rkM{L%yytRD|1KIi+FU*a#hCY?!ZSeU7_bKowv8%ev@`n z?9H$x?>GCpw7NM`mbT;V}|QLJg^dc@I}QHy)Nz4u)2~mv<-E6 zp$(fpx9t~OJz1_0=_0XZ7cCoEL3LoY7JtIx^wIbm+pG5K87s3A z^M{Xp{t(OJwGo2%fSxjT;)FN#{n{>{sM?ClwHGpKD^hAJ!fP)a@ff$BZsxqxcyiXp zEZ0;)Dg^kd?&9$;(sX2SsxNi;`k+JRIrL;#g8g2Eo_s}$vLhL`BU%0Y@jSlr6OAUt zxs#u`a>bS{MPHUx=U5!Wl)l`O*VNyDV3s58vSsO>Z*fn&l)vjpW*3z?s$2D1&W!VF z9(njt^+k@N+RLMgsmfVzPdBAXbkn6_$SdC*Qzy=t053gsNn3PmRLm-uuzO4Tj&zgk z4dYxiYuX9b8>(h$ZD~R&<@$@&pI>a=aaBB9;96k%3dHps?OrLW_RisWkv#HZ>1G6I zoX1O^$@SC@c}9(`T8opH>+$kMIC&3B6Ca5D z%G;x=1BHS%Rbqe4XZoRqMx4V zlxGp8yUZ}~IHW{q+5vl)7aL3>8VtZC>3qT2)HKD-#+{F$K48`;@JZzgjgXb|cae{d zBERZ-P2{#>o30Y^`|Gi&dv)g8Sad zZD8ieSLNcH7PH%~tRTXgZe#VRH;#p|XwHn+K;}Pr4I{6zXw7t#&MK1x_DY+f>?{OP zGg4IFeB-zIRI=0yH$zrxZJ*Ag!^9a2d#dz@V6#v?!1C0H(& z2lu)9X5Wc)&bx^OZqXeLTiJ?TAgXz);?2ZC9P{o0A8=>eC1EwYU!7>O>aw$1qyeKvG;pY0=Er zJ~3L=ZOh}=h^t(p&W>y84L6!OD_1ALWHU!yS0iWPNHvo>NL$0Hpl37_qN=-^oq;s4 zw>)NYx<8Sq&Z0K_&O^0i!MV01|CxJ1p7t>uSR4IUY2I;#_fwXFSzd8qIR7Ng7=bHF_g5?qCfHJPW0FWOHD# z{r=R#{3=DlGq0tDt?wnO8mMv^&E_A^Nmvo!U}i0Rr{(2je2kef-?F#`Gc;?*$lOXl zR7_~-SCo;c+b<%oG_NMYjBak1Q}UEi=#itdS5Q9-uw*THS7FBk1Q^8=$j`V{m{Zw3 z(;ztGhP_THF(4UEJu(yzH5ApiX+;X_7s}cQE98U`FqdiM5FDF#$_}9=BrAKY76Qb$ z+hso~4j`Jb2T5ceRCWD+uFL(8kvHm`{?>vo>0 zy6AF}ruwx`(WL>8RCPsU1$9bc*}EHLaC8gTx}C%sRh7KOP&^OyyrTgE%EVZousRfh z2M=5=S1mjQU0U+g#g_btSEx^Y#_*c4jfk2xFT^0{MB**@b;B>_HqJS;;>iZo4%4Ka zOzK^1WBD2!w&yX}w+;^2@Dy{zV0ekz&6I8_*%T7K_RztbOSRF5dDKs1-fQt{c1u2k zlZ0KjJk=0%=Gpen>f;?p!tD-@qiYgLl_o3)F@-Q3TJI;;?FZiT-=Me(I^@ZlHSh>} zxum#ruAcByByJqr@l-H-1sWP3v~yav<&|)zNnrTEkZQs2%wy9bHlQzf@1aU%;fx)(zN<;TRT(Nk6#Is{?O7tAXDMse*sN3(SG7E z2c~S;Jmcv^(HhI4d^A+jO;Eh*2fA>2E? z_DzJBs3>}D=w~Y_rEgwpV3Vl7;j?Dvukg*wAlA63QXP0$hSa&93CyS$TmR7%_y? zvlX;VOscTlMFlP08MXDV^HUedKax0&R{K0ZvpxO7I>umWlj1$!{F6V-FfNMn=UV)a z@U1dhdS|t@f8tw#NKTvoBES`fqZk2V4D~=jWZ;3Y&)pkpFPap9dHzGM4gg&L)SZ8e za0LPsac%l7-V?xg;$*)-x4T_9IMRPcxE^fH-$7i3gupY%5HPMUxHK97*W=_!pmjJM;2H!Dlm!<;fe;WF2)-+a17+gc2oUQ4`M#(BLjA(` z-jY92zc5@Qeu91h7&H8zA^ctEd^Jo4>33dlW_)yZ`uc?9GGV*5@9AR{?Ht^Zr%Z_z zPUJp`mr)4$+-&V%hJS@kq2;tC?fu(yxit?vt4<>WFIIBDIFVNDe6`yX&e>fRn}V_T zN{?1^wo&OU@DL>IRh!FN_}W$begvS=@ow^9!;eV9!>`USUPH>9#nU1RHaKlP_dKfZ zrOT0gc032gb&Pa_k2PMrC*JyllTP|m4TF~mTev z>=3tS;TIRrE^1LsrLP^zJ{P>=W8xp!@n%Cpf-FLUV91Ph#wncnVlX3K=u4rm1FE9c z@92Z;@r)gc$!5k`8+!4Fc=1LDuo{dJx=*Ep7r?<)SCj5CYtn$%%?^EnKNuv$yS3_? zwq$rnWLTE$JzkA>*K!q+p~L%awwFXhO?dfrt2NzsBOA32y&XCP%4^K43G|O!AbC4P z@aZ+!gCbed7~v@?-a6+6B2*{SAu6i=nnH1kj>k!+cV=hpFwGmW!<>9h zV7qYkNgN+jjB2;=Amoh-Ihupt< zkC-p#-$ASQ!z=)&q%V=#GJ^j-{!8){Z@XbHapMcBS#* zL{@1rLzsI`)+%Y0jrZy-n1>OwsaDH$EVBni9vd+NPH!&l+#lVx+~FfSjT7TN5doIm&DU* zKAx8`3yD=nx3{$c8On=T7#}GEvCl|<#^fwOE4pp3JunYscLKVbdn#HD zU)?;sp57J0MtJ(9=~IISFi$eA!dyw}hkTM1>7=XnG*^aVvWsa4*-8>mcUtmq+F!QxlR$DWwG%A3;V2XzAm7}|C z;(G%=hgH;zKGjCP6j4%ZEJG10xs~b6~OQzrJzfA3BH&$cNn=mZ^tliUXWPbB@q!QBwdGhRQcBIVA z3GgnSSf-0h3BUWi&sRmyq0(#$53_2 zjOHV2!MAS~Z%hS0mgWYg4%Kr-e)Y5O;cywdA>%K{iK#le6p?mOBE75L%MX>tk}69< zjwLW3D4!d3#eVA|dBdt+zrpUCoVCF|+cd`ABzV;?&+a|mmxuP@QHu+@>tm$@jbfMU z&kbivnskwPpVlmrVkUBSjC<=$R4uPj&pEtNTy1^$0aCMr#KDE+y-ir^rmQv!Vb5MX z$JC${LzXvcr*SrrMkreA?4#oHW%oduLC4%~tZkjY`6dhi^?<QYCZ4mQrl2_|}tw9WjmrAFy`;w?o*W5`(0i`|}3*OT*y`%6{s;n-L z=RBrsDDkuj(p3I>pBkzA)YrcFplSf97G!=YUY-g9q2>=@5>s-~8gh=8COH#EJvD%K z2xbq9;~={hZ^Cq3{&*^}5Vy)jw_8asWdw@4?~1#Bn5?LOHO^L_<}`0>#9H|YvZ)x~ z+$^6TKhIv0em)+~K3h+r5Vh@!zcWX+rKdo+efZSFhgAm+A^1TOLmVCZzX-zoI~pS3 zQ{XQn<+^d8-S><;#%hmFrJyw91PrLo7R5EXdQP_8s5zMh~ukd?U*@&dJ_>5$%Kxvpn zJ|`$1-MVu2mXhiDuM-w=>HrauKR`r8R#9K$=QxWlV2o8bOsGx5n zy`rV%x4)SKUw=-A%Zt2^)_KuZ)^s~ha&67zTFGsP00N@Vo~#Ea0roRW?$>bv->HWF zG1^jIUS3X4{{PDw{SoV@hqG=DSfjfuX|Lr6m;t{Q!XIty1J>xDy7RBC(eELkzgeTZ zYjJOz04wZIZThn{dayOXeeuVD%l|)Xbk|DSYmMT}xnIWU?!@4%ZQS__fVsA7>-}Y7 z?R5{xK|#ALQ9xO8FwkyjEG`K!nEsesIXGao?N0Na;`fZ66l9m+>ifk0I+sH5tQ4Rm zXlV|D;Myf91^s(90f6r4mm=(M6(M`o;D4(I2F{wmm52K;F1}|}_QaF^>dQ~F9k}Oy z3HLhwQ~~~|A7h~k@JZpeHQc}h>3xOekh~9rV>B2f5LQVi7CiFM0WPN<7dueL;=K9q%ZmsP% za@l$-gTg8`zl!+-M z`vx0B-T6lglFprI#tz0FERRU2TkL0wF7N0LErIeto^Z2k75bA~M|?$TL{h|7qGqJb zB$;IC4sK?<1NDC%0T_mLRHrqMnD)%XPbg0M$uM_Zyl zkmT*o-M?ua$zSt>6hNYdzz8H%2yk-n3kjg0C^%FI0tK8;zg_z6%CEUZ2>mBL!6N$Z zgYSVMP`&mk=m6|K9H?0vtkFIW)d=pGyfB-qV12<2?S_w6K2DnZhr!>os(&0j^S@Yr zu>YXReFzQQuunm?fJqqd3)A>9`g(sF{k<-p?;FSW({Kcp?X5h3{c88vnO%?KSywNB zux)n>`N7K!-*XoWo&`>O!SUj8tD1XWaB}r@vbO+Tgb6?(0uVlsou}so4`D$;=fAEA zxVqc&kO*qHyIOf!0;k99&33`c24rDw=?GNwvj{+Sw*q@t7a81HJKQqD0F^TmjDiS3 z5Ku4}at4g%0)x4D_Us%!?$$OWU=SQh0{-g-c<&H^+s+2`mkhUG;9ek?A2KN5Edw^4 z-(?6W5Ej2*2E%RF`}7ciTkU`hN4d9O5BDJaO$NLHTygtlFr2gVcRd&avA+&57z#N4 z4(Q=Ly}#v!0?xaGZAJmlql>VO^?KPVh_aQz|Sd)Jz$ zyE#DI=)QX