diff --git a/README.md b/README.md index ea16dd0..f87c0a6 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,11 @@ Rayleigh MSE), `E4` writes `csi_error.csv` (imperfect-CSI robustness), `E5` writes `mask_family_rev.csv` (Walsh–Hadamard versus Haar), `E8` writes `mismatch.csv` (affinity mismatch and quantization), and `E9` writes `cosine_ceiling.csv` (cosine-ceiling -corollary check). +corollary check). The empirical affinity statistics quoted in the +manuscript are recomputable from `clip_realdata_beta.csv` and +`bert_vit_beta.csv` (32 paired and 32 unpaired samples per encoder +family), and the trained refinement gates behind the capacity-check +claim are stored in `refine_gates.npz`. ## Verifying the analysis diff --git a/code/refine_matched.py b/code/refine_matched.py index 81e48b5..faa22b4 100644 --- a/code/refine_matched.py +++ b/code/refine_matched.py @@ -127,7 +127,14 @@ def train_refiners(epochs=220, steps=20, batch=48, lr=5e-4, print(f" epoch {ep+1}: loss {float(loss.detach()):.4f} " f"(cos {float(cs.detach()):.3f})", flush=True) print(f" trained in {time.time()-t0:.0f}s") - return P_single, [p.detach() for p in params] + P4 = [p.detach() for p in params] + rel = float((P_single - sum(P4) / 4).norm() / P_single.norm()) + print(f" [diag] ||P1 - mean(P4)|| / ||P1|| = {rel:.3e}") + np.savez(DATA / "refine_gates.npz", + P1=P_single.cpu().numpy(), + **{f"P4_{i}": p.cpu().numpy() for i, p in enumerate(P4)}) + print(" [diag] gates saved to data/refine_gates.npz") + return P_single, P4 def refine_apply(ps, z): diff --git a/code/replot_all.py b/code/replot_all.py index 8b5796e..2df1773 100644 --- a/code/replot_all.py +++ b/code/replot_all.py @@ -57,6 +57,7 @@ def save(fig, name): # ------------------------------------------------------ fig_floor def fig_floor(): + from matplotlib.lines import Line2D rows = rows_of("floor_validation") fig, ax = plt.subplots() colors = {"256": "C0", "768": "C3"} @@ -66,12 +67,10 @@ def fig_floor(): or float(r["d"]) == float(d)] snr = col(rd, "snr_db") ax.plot(snr, col(rd, "mse_mc"), "o", ms=3.5, color=colors[d], - mfc="none", label=rf"Monte Carlo, $d={d}$") - ax.plot(snr, col(rd, "mse_theory"), "-", color=colors[d], - label=rf"Theorem 1, $d={d}$") + mfc="none") + ax.plot(snr, col(rd, "mse_theory"), "-", color=colors[d]) if d == "768": - ax.plot(snr, col(rd, "mse_blind"), "--", color="C1", lw=1.2, - label=LBL["blind"]) + ax.plot(snr, col(rd, "mse_blind"), "--", color="C1", lw=1.2) g = 1.0 - beta**2 ax.axhline(math.sqrt(g) / 2, color="gray", lw=0.8, ls="--") ax.axhline(0.5, color="gray", lw=0.8, ls=":") @@ -81,12 +80,25 @@ def fig_floor(): fontsize=7, color="gray") ax.set_xlabel("Per-block SNR $\\rho$ [dB]") ax.set_ylabel(r"Per-user MSE $\mathbb{E}\|\hat{\mathbf{e}}_u-\mathbf{e}_u\|_2^2$") - ax.set_xlim(0, 40); ax.set_ylim(0.4, 1.32) - ax.set_yticks([0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]) - # legend in dedicated headroom above the curves (max 1.0), no overlap - ax.legend(loc="upper center", ncol=2, columnspacing=0.9, - handlelength=1.6, borderaxespad=0.3) - save(fig, "fig_floor") + ax.set_xlim(0, 40); ax.set_ylim(0.4, 1.05) + # one-row legend fully OUTSIDE the axes, flush to the top-right: + # composite handles (marker = Monte Carlo, line = Theorem 1; the + # convention is stated in the caption), so three entries fit one row + handles = [ + Line2D([], [], color="C0", marker="o", mfc="none", ms=3.5, + ls="-", label="$d=256$"), + Line2D([], [], color="C3", marker="o", mfc="none", ms=3.5, + ls="-", label="$d=768$"), + Line2D([], [], color="C1", ls="--", lw=1.2, label=LBL["blind"]), + ] + ax.legend(handles=handles, loc="lower right", + bbox_to_anchor=(1.0, 1.0), ncol=3, frameon=False, + columnspacing=1.0, handlelength=1.8, borderaxespad=0.0, + handletextpad=0.5) + fig.subplots_adjust(left=0.205, right=0.965, top=0.90, bottom=0.185) + fig.savefig(FIG / "fig_floor.pdf") + plt.close(fig) + print("[OK] wrote fig_floor.pdf") # ------------------------------------------------ fig_rate_corrected @@ -124,7 +136,7 @@ def fig_beta_sweep(): ax.plot([], [], ls="--", color="gray", label=LBL["oma"]) ax.plot([], [], ls="-.", color="gray", label=LBL["genie"]) for b0 in (0.030, 0.311): - ax.axvline(b0, color="gray", ls=":", lw=0.9) + ax.axvline(b0, ymax=0.62, color="gray", ls=":", lw=0.9) ax.set_xlabel(r"Pairwise affinity $\beta$") ax.set_ylabel("Effective sum rate [bps/Hz]") ax.set_xlim(0, 1); ax.set_ylim(0, 1.0) @@ -168,6 +180,7 @@ def fig_multiuser(): ax.set_xlabel("Per-block SNR $\\rho$ [dB]") ax.set_ylabel("Effective sum rate [bps/Hz]") ax.set_xlim(0, 30) + ax.set_ylim(bottom=0) ax.legend(loc="upper left") save(fig, "fig_multiuser_corrected") diff --git a/code/replot_merged.py b/code/replot_merged.py index f5ae413..a90a040 100644 --- a/code/replot_merged.py +++ b/code/replot_merged.py @@ -7,6 +7,7 @@ from pathlib import Path import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt +from replot_all import LBL ROOT = Path(__file__).resolve().parents[1] plt.rcParams.update({ @@ -25,17 +26,17 @@ snr = [float(r["snr_db"]) for r in rows] col = lambda k: [float(r[k]) for r in rows] fig, ax = plt.subplots() -ax.plot(snr, col("edma"), "o-", color="C3", label="EDMA") +ax.plot(snr, col("edma"), "o-", color="C3", label=LBL["edma"]) ax.plot(snr, col("edma_ref"), "^-", color="C2", - label="EDMA + refinement stage") -ax.plot(snr, col("todma"), "d-.", color="C4", label="ToDMA-adapted") -ax.plot(snr, col("oma"), "v:", color="C1", label="OMA") + label=LBL["hybrid"]) +ax.plot(snr, col("todma"), "d-.", color="C4", label=LBL["todma"]) +ax.plot(snr, col("oma"), "v:", color="C1", label=LBL["oma"]) ax.plot(snr, col("genie"), "-", color="gray", lw=1.0, - label="Genie-aided SIC bound") + label=LBL["genie"]) ax.set_xlabel("Per-block SNR $\\rho$ [dB]") ax.set_ylabel("Mean cosine similarity") ax.set_xlim(snr[0], snr[-1]) -ax.set_ylim(0, 0.85) +ax.set_ylim(0, 0.75) ax.legend(loc="upper left") fig.subplots_adjust(**AXES_RECT) fig.savefig(ROOT / "fig" / "fig_bertvit_merged.pdf") diff --git a/data/bert_vit_beta.csv b/data/bert_vit_beta.csv new file mode 100644 index 0000000..1ee0147 --- /dev/null +++ b/data/bert_vit_beta.csv @@ -0,0 +1,65 @@ +scheme,beta +paired,0.027237803175426414 +paired,0.012896661680831107 +paired,0.010416352482946263 +paired,0.048123426570364544 +paired,0.02378884879050521 +paired,0.08129956173740625 +paired,0.019008848452584104 +paired,0.008772706088568172 +paired,0.005386448106588374 +paired,0.04672583614390642 +paired,0.02654219206213419 +paired,0.008971354550608865 +paired,0.006527803719580855 +paired,0.0003781749165654546 +paired,0.040345337710264106 +paired,0.06631996053522116 +paired,0.023303015182582845 +paired,0.0036342242085544724 +paired,0.01080984433510444 +paired,0.05122711771786337 +paired,0.02196780882017706 +paired,0.027430766747928603 +paired,0.05481789805580116 +paired,0.09104140849083153 +paired,0.01815058092411296 +paired,0.03022733891851148 +paired,0.007488021664737729 +paired,0.0023511041567845174 +paired,0.09862665895072077 +paired,0.048736810747693156 +paired,0.011754320764163387 +paired,0.036121899180464434 +unpaired,0.019374828604252024 +unpaired,0.032440294233851884 +unpaired,0.02378860498148811 +unpaired,0.008587382855481418 +unpaired,0.05651731568397114 +unpaired,0.02403075688014663 +unpaired,0.03458268967264902 +unpaired,0.03784547767683299 +unpaired,0.0027191357506518334 +unpaired,0.06647789537369261 +unpaired,0.028248259683892177 +unpaired,0.02975719499694031 +unpaired,0.002693260517757482 +unpaired,0.00956545888624823 +unpaired,0.018776037962855974 +unpaired,0.01723862993724272 +unpaired,0.003321530987236865 +unpaired,0.026794936423899428 +unpaired,0.005555610068283409 +unpaired,0.02096161483617256 +unpaired,0.029145082852780703 +unpaired,0.008083174012553023 +unpaired,0.009863492000612803 +unpaired,0.016990287948247137 +unpaired,0.009933587496519476 +unpaired,0.06247402736383933 +unpaired,0.020214432381559597 +unpaired,0.06931381025435437 +unpaired,0.02564802276256673 +unpaired,0.04367255778838598 +unpaired,0.01957917212315197 +unpaired,0.06779897449994185 diff --git a/data/clip_realdata_beta.csv b/data/clip_realdata_beta.csv new file mode 100644 index 0000000..1a69b64 --- /dev/null +++ b/data/clip_realdata_beta.csv @@ -0,0 +1,65 @@ +scheme,beta +paired,0.2875853478908539 +paired,0.33386528491973877 +paired,0.3495585322380066 +paired,0.2988513112068176 +paired,0.3365963399410248 +paired,0.28957056999206543 +paired,0.3398767411708832 +paired,0.2994540333747864 +paired,0.3134581446647644 +paired,0.2800610363483429 +paired,0.33044949173927307 +paired,0.3525742292404175 +paired,0.28622955083847046 +paired,0.3221920132637024 +paired,0.34046676754951477 +paired,0.3445603847503662 +paired,0.3361469507217407 +paired,0.315978467464447 +paired,0.2871871590614319 +paired,0.20464202761650085 +paired,0.33651721477508545 +paired,0.28726524114608765 +paired,0.26744377613067627 +paired,0.2686237692832947 +paired,0.2603684663772583 +paired,0.32788804173469543 +paired,0.28169411420822144 +paired,0.3227519690990448 +paired,0.3319033086299896 +paired,0.3185873031616211 +paired,0.34493112564086914 +paired,0.3604525923728943 +unpaired,0.13933990895748138 +unpaired,0.10618321597576141 +unpaired,0.16058675944805145 +unpaired,0.17256049811840057 +unpaired,0.18050867319107056 +unpaired,0.17393678426742554 +unpaired,0.17463842034339905 +unpaired,0.12813161313533783 +unpaired,0.14834891259670258 +unpaired,0.16522526741027832 +unpaired,0.15986862778663635 +unpaired,0.19971923530101776 +unpaired,0.21379850804805756 +unpaired,0.16859294474124908 +unpaired,0.1630430519580841 +unpaired,0.1932932436466217 +unpaired,0.1589049994945526 +unpaired,0.15467114746570587 +unpaired,0.21967162191867828 +unpaired,0.1271267682313919 +unpaired,0.1379617303609848 +unpaired,0.11341479420661926 +unpaired,0.1405247300863266 +unpaired,0.1693478673696518 +unpaired,0.2000925987958908 +unpaired,0.1594662368297577 +unpaired,0.1274571418762207 +unpaired,0.18167132139205933 +unpaired,0.14616280794143677 +unpaired,0.1484544575214386 +unpaired,0.14663159847259521 +unpaired,0.1430159956216812 diff --git a/data/refine_gates.npz b/data/refine_gates.npz new file mode 100644 index 0000000..6f1f7e6 Binary files /dev/null and b/data/refine_gates.npz differ diff --git a/fig/block_diagram.pdf b/fig/block_diagram.pdf index 92caeb4..005199f 100644 Binary files a/fig/block_diagram.pdf and b/fig/block_diagram.pdf differ diff --git a/fig/block_diagram_src.tex b/fig/block_diagram_src.tex index 8d1d87d..9d8ad28 100644 --- a/fig/block_diagram_src.tex +++ b/fig/block_diagram_src.tex @@ -6,7 +6,7 @@ \usetikzlibrary{arrows.meta,positioning,fit,calc} \begin{document} \begin{tikzpicture}[ - font=\footnotesize, + font=\small, node distance=3.2mm and 4.5mm, blk/.style={draw, semithick, minimum height=5.5mm, minimum width=9mm, inner sep=1.5pt, align=center}, @@ -58,8 +58,8 @@ \draw[arr] (sig) -- node[above, lbl] {$\mb{r}$} (mf); \draw[arr] (mf) -- node[above, lbl] {$\mb{t}_u$} (wnr); \draw[arr] (wnr) -- (out); -\fill (tap1) circle (0.5pt); -\fill (tapU) circle (0.5pt); +\fill (tap1) circle (1.1pt); +\fill (tapU) circle (1.1pt); \draw[dsh] (tap1) -- ($(tap1 |- bm.north)$); \draw[dsh] (bm.east) -| (wnr.south); \end{tikzpicture} diff --git a/fig/fig_bertvit_merged.pdf b/fig/fig_bertvit_merged.pdf index d9a7e58..3479a09 100644 Binary files a/fig/fig_bertvit_merged.pdf and b/fig/fig_bertvit_merged.pdf differ diff --git a/fig/fig_beta_sweep_corrected.pdf b/fig/fig_beta_sweep_corrected.pdf index 22c9162..b1e5859 100644 Binary files a/fig/fig_beta_sweep_corrected.pdf and b/fig/fig_beta_sweep_corrected.pdf differ diff --git a/fig/fig_floor.pdf b/fig/fig_floor.pdf index a099679..d915bdd 100644 Binary files a/fig/fig_floor.pdf and b/fig/fig_floor.pdf differ diff --git a/fig/fig_multiuser_corrected.pdf b/fig/fig_multiuser_corrected.pdf index 0a2c038..8f5e3d9 100644 Binary files a/fig/fig_multiuser_corrected.pdf and b/fig/fig_multiuser_corrected.pdf differ