diff --git a/README.md b/README.md index cd1d1aa..ea16dd0 100644 --- a/README.md +++ b/README.md @@ -10,66 +10,78 @@ This repository contains the simulation code, the raw result data, and the figure files behind every numerical claim in the paper. It is private during peer review and will be made public upon publication. +The design under test: each user applies an independent Haar +orthogonal mask, and the receiver runs a matched filter followed by +the closed-form affinity-aware Wiener demultiplexer, which harvests +the coherent interference component that the measured pairwise +affinity `beta` predicts. The affinity-blind reference sets `beta = 0` +in the same filter. + ## Layout | Folder | Contents | |---|---| -| `code/` | Simulation and plotting scripts (Python, CPU only) | +| `code/` | Simulation and plotting scripts (Python) | | `data/` | Raw results written by the scripts, one CSV per experiment | -| `fig/` | Figure PDFs included in the manuscript | +| `fig/` | Figure PDFs included in the manuscript (`block_diagram_src.tex` is the TikZ source of Fig. 1) | ## Requirements -Python 3.10 or later with `numpy` and `matplotlib`. The Fig. 4 -experiment additionally uses `torch` (CPU build is sufficient). No GPU -is required. Every script fixes the seed 2026 and writes its raw output -to `data/`, so plotting is decoupled from simulation. +Python 3.10 or later with `numpy` and `matplotlib`. The Monte Carlo +experiments in `revision_sims_gpu.py`, `fig_real_merged.py`, and +`refine_matched.py` use `torch` (CUDA when available; the scripts fall +back to CPU). All random draws come from the numpy generator with the +fixed seed 2026 — torch only accelerates QR, matrix products, and +linear solves — and every script writes its raw output to `data/`, so +plotting is fully decoupled from simulation. ## Reproducing the figures -Run the scripts from inside `code/`. +Run the scripts from inside `code/`. All plots are rendered from +`data/` only, by `replot_all.py` (Figs. 2, 3, 5, 6, 7) and +`replot_merged.py` (Fig. 4). -| Figure | Content | Script | Data | +| Figure | Content | Simulation | Data | |---|---|---|---| -| Fig. 2 | Per-user MSE and self-interference floor | `revision_sims.py E1` | `floor_validation.csv` | +| Fig. 1 | System diagram | `latexmk -pdf fig/block_diagram_src.tex` | — | +| 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`; replot with `replot_merged.py` | `bertvit_merged.csv` | -| Fig. 5 | Realizable versus genie-aided SIC | `revision_sims.py E2`; replot with `replot_sic.py` | `sic_comparison.csv` | -| Fig. 6 | Affinity sweep and crossover | `revision_sims.py E7a` | `beta_sweep_corrected.csv` | -| Fig. 7 | Multi-user scaling | `revision_sims.py E7c` | `multiuser_corrected.csv` | - -Fig. 1 is a system diagram and has no simulation behind it. +| 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` | Quantities quoted in the text but not plotted come from the same -driver: `revision_sims.py E0` writes `theorem_check.csv` (Theorem 1 -constants), `E4` writes `csi_error.csv` (imperfect-CSI robustness), and -`E5` writes `mask_family_rev.csv` (Walsh–Hadamard versus Haar masks). -`revision_sims.py` with no argument runs every experiment. +drivers: `revision_sims.py E0` writes `theorem_check.csv` (Theorem 1 +validation across affinities and channel phases), +`revision_sims_gpu.py E3` writes `rayleigh_mse.csv` (unconditional +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). ## Verifying the analysis -`verify_math.py` re-derives every closed-form expression in the paper -numerically and prints one PASS/FAIL line per item, covering the -per-realization Gram identity, Theorem 1 and its self-interference -constants, the effective-SINR corollary, the MAC-consistency -proposition, the wideband limit, both crossover conditions, the -affinity-mismatch bound, the CSI-invariance identity, the multi-user -inverse formula, and the Walsh–Hadamard construction. It depends only -on `numpy`. +`verify_math.py` re-derives every closed-form claim numerically and +prints one PASS/FAIL line per item: Theorem 1 at the equal-gain point +and under random channel phases for both users, the aware and blind +error floors and the value-of-affinity ratio, the cosine-ceiling +corollary, the blind-receiver/matched-filter cosine equivalence, the +monotonicity proposition, the full-cooperation bound with its +equality case at `beta = 1`, the finite-SNR MAC-condition boundary, +the exact Walsh–Hadamard closed form, the quadratic mismatch +stationarity, and the dominated floor of the correlated-mask +alternative from the Appendix. It depends only on `numpy`. ## Conventions The scripts follow the manuscript exactly: unit per-block transmit energy `E_b = 1` per user, `rho = E_b / sigma_n^2` as the per-block SNR with per-symbol SNR `rho/d`, complex block-Rayleigh gains unless -the evaluation point `h_u = 1` is stated, real unit-norm embeddings, -and masks drawn fresh from the Haar mixture on every realization. - -`fig_real_merged.py` also produces columns for a retrained -attention-based receiver. Those columns are kept in `bertvit_merged.csv` -for completeness but are not used by any figure in the paper. -`revision_sims.py E6` covers a high-affinity combining mode that is -outside the scope of this paper. +the evaluation point `h_u = 1` is stated, real unit-norm embeddings +with the orientation ` = +beta`, and independent Haar masks +drawn fresh on every realization. ## Citation and license diff --git a/code/fig_real_merged.py b/code/fig_real_merged.py index 17fdf5e..060315d 100644 --- a/code/fig_real_merged.py +++ b/code/fig_real_merged.py @@ -1,30 +1,30 @@ """ -Merged real-data comparison figure (replaces separate Figs 4 and 5). +Real-data comparison on cached BERT (text) + ViT (image) pairs. =================================================================== -Evaluates ALL schemes on the cached real BERT (text) + ViT (image) -embedding pairs (16 pairs, d = 768, measured mean affinity ~0.028) -under the manuscript's complex block-Rayleigh channel: +Evaluates the schemes on the cached real embedding pairs +(16 pairs, d = 768, measured mean affinity ~0.028) under the +manuscript's complex block-Rayleigh channel: r = h1 M1 e1 + h2 M2 e2 + n, n ~ CN(0, sigma^2 I), h_u ~ CN(0,1), per-block energy E_b = 1, rho = 1/sigma^2 (per-block SNR). -Schemes: - 1. EDMA : per-realisation Haar-mixture masks with the per-pair - measured beta_i, closed-form demux (13). +Schemes (v2 design: independent Haar masks per user): + 1. EDMA : affinity-aware Wiener demultiplexer with the + per-pair measured beta_i. 2. OMA : equivalent-bandwidth model, noise std x sqrt(2). 3. Genie SIC : perfect removal of the other user's waveform. - 4. Attention : retrained reproduction of the learned predecessor, - d = 768, trained on parametric pairs at the measured - mean affinity with Rayleigh channels and - channel-equalised matched-filter inputs - x_u = Re(M_u^T r / h_u); evaluated on the REAL pairs. - 5. ToDMA-adapted: OMP sparse coding of the real embedding (T = 16 + 4. ToDMA-adapted: OMP sparse coding of the real embedding (T = 16 atoms, V = 1024), T slots x L = 48 signatures, per-slot OMP detection on the complex observation, genie association, true coefficients granted. -Outputs: fig/fig_bertvit_merged.pdf, data/bertvit_merged.csv. -200 fading realisations per pair -> 3,200 Monte-Carlo samples per SNR. +The hybrid (EDMA + refinement stage) curve is produced separately by +refine_matched.py (torch) and merged by replot_merged.py. + +EDMA/OMA/genie run in torch (CUDA when available, batched over the +SNR grid); the ToDMA detector runs in numpy on the CPU. Run under +WSL for GPU acceleration. Outputs: data/bertvit_merged.csv. +NFADE fading realisations per pair; ToDMA uses the first 40. Seed fixed. """ from __future__ import annotations @@ -35,38 +35,20 @@ import time from pathlib import Path import numpy as np import torch -import matplotlib -matplotlib.use("Agg") -import matplotlib.pyplot as plt ROOT = Path(__file__).resolve().parents[1] DATA = ROOT / "data" FIG = ROOT / "fig" -plt.rcParams.update({ - "font.family": "serif", - "font.serif": ["DejaVu Serif", "Times New Roman"], - "font.size": 9, "axes.labelsize": 9, "legend.fontsize": 6.6, - "xtick.labelsize": 8, "ytick.labelsize": 8, - "axes.grid": True, "grid.linestyle": "--", "grid.linewidth": 0.4, - "grid.alpha": 0.6, "lines.linewidth": 1.4, "lines.markersize": 4.0, - "figure.figsize": (3.15, 2.36), "pdf.fonttype": 42, -}) -AXES_RECT = dict(left=0.205, right=0.965, top=0.955, bottom=0.185) - SEED = 2026 rng = np.random.default_rng(SEED) torch.manual_seed(SEED) +DEV = "cuda" if torch.cuda.is_available() else "cpu" D = 768 -SNRS = np.arange(0.0, 31.0, 5.0) -NFADE = 200 # fading realisations per pair - - -def haar(d): - G = rng.standard_normal((d, d)) - Q, R = np.linalg.qr(G) - return Q * np.sign(np.diag(R)) +SNRS = np.arange(0.0, 31.0, 2.5) +NFADE = 100 # fading realisations per pair +NFADE_TOD = 40 # ToDMA heavier: first 40 draws def unit(v): @@ -87,100 +69,35 @@ def load_pairs(): return a, b, betas -# ------------------------------------------------------------------ -# attention model: trained at the measured mean affinity, d=768, -# Rayleigh channels, channel-equalised MF inputs -# ------------------------------------------------------------------ -EPS_EQ = 0.1 # regularised equalisation h*/(|h|^2+EPS_EQ): - # caps deep-fade amplification for the learned readout +def haar_t(n, gen): + G = torch.randn(n, D, D, generator=gen, device=DEV) + Q, R = torch.linalg.qr(G) + return Q * torch.sign(torch.diagonal(R, dim1=-2, dim2=-1)).unsqueeze(-2) -def train_attention(beta0, epochs=150, steps=20, batch=48, lr=5e-4, - l1=1.0, l2=0.5, l3=0.5): - print(f"=== training attention reproduction (d={D}, beta={beta0:.3f}, " - f"{epochs} epochs, Rayleigh) ===", flush=True) - gen = torch.Generator().manual_seed(SEED) - g0 = math.sqrt(1.0 - beta0**2) - - def torch_pairs(n): - e1 = torch.nn.functional.normalize( - torch.randn(n, D, generator=gen), dim=1) - w = torch.randn(n, D, generator=gen) - w = w - (w * e1).sum(1, keepdim=True) * e1 - w = torch.nn.functional.normalize(w, dim=1) - return e1, beta0 * e1 + g0 * w - - M1 = torch.nn.Parameter(torch.linalg.qr( - torch.randn(D, D, generator=gen))[0]) - M2 = torch.nn.Parameter(beta0 * M1.detach() - + g0 * torch.linalg.qr( - torch.randn(D, D, generator=gen))[0]) - Q1 = torch.nn.Parameter(torch.randn(D, D, generator=gen) / math.sqrt(D)) - Q2 = torch.nn.Parameter(torch.randn(D, D, generator=gen) / math.sqrt(D)) - opt = torch.optim.Adam([M1, M2, Q1, Q2], lr=lr) - eye = torch.eye(D) - - t0 = time.time() - for ep in range(epochs): - for _ in range(steps): - e1, e2 = torch_pairs(batch) - snr_db = 5.0 + 20.0 * torch.rand(batch, 1, generator=gen) - sig = 10 ** (-snr_db / 20.0) - hr = torch.randn(batch, 2, generator=gen) - hi = torch.randn(batch, 2, generator=gen) - # complex channel on real signals; equalised MF real part: - # x_u = Re(M_u^T r / h_u); build via real/imag components - s1 = e1 @ M1.T - s2 = e2 @ M2.T - nr = sig * torch.randn(batch, D, generator=gen) / math.sqrt(2) - ni = sig * torch.randn(batch, D, generator=gen) / math.sqrt(2) - rr = (hr[:, :1] * s1 + hr[:, 1:2] * s2) / math.sqrt(2) + nr - ri = (hi[:, :1] * s1 + hi[:, 1:2] * s2) / math.sqrt(2) + ni - outs = [] - for u, (Mu, Qu) in enumerate(((M1, Q1), (M2, Q2))): - hu_r = hr[:, u:u+1] / math.sqrt(2) - hu_i = hi[:, u:u+1] / math.sqrt(2) - mag = hu_r**2 + hu_i**2 + EPS_EQ - xr = (rr @ Mu) - xi = (ri @ Mu) - xu = (xr * hu_r + xi * hu_i) / mag # Re(h* r'/(|h|^2+eps)) - sc = (xu @ Qu.T) / math.sqrt(D) - outs.append(D * torch.softmax(sc, dim=1) * xu) - gram = ((M1.T @ M1 - eye)**2).mean() \ - + ((M2.T @ M2 - eye)**2).mean() \ - + ((M1.T @ M2 - beta0 * eye)**2).mean() - mse = ((outs[0] - e1)**2).mean() + ((outs[1] - e2)**2).mean() - cs = torch.nn.functional.cosine_similarity( - outs[0], e1, dim=1).mean() \ - + torch.nn.functional.cosine_similarity( - outs[1], e2, dim=1).mean() - loss = l1 * gram + l2 * mse + l3 * (2.0 - cs) - opt.zero_grad(); loss.backward() - torch.nn.utils.clip_grad_norm_([M1, M2, Q1, Q2], 1.0) - opt.step() - if (ep + 1) % 50 == 0: - print(f" epoch {ep+1}: loss {float(loss.detach()):.4f}", - flush=True) - print(f" trained in {time.time()-t0:.0f}s, " - f"{4*D*D/1e6:.2f}M parameters") - return (M1.detach().numpy(), M2.detach().numpy(), - Q1.detach().numpy(), Q2.detach().numpy()) +def aware_batch(t, Q, beta, c, nvar): + """Batched affinity-aware Wiener demux. t: (b,D) cfloat, Q: (D,D), + c: complex scalar, nvar: (b,) real.""" + b = t.shape[0] + g = 1.0 - beta * beta + rho = g * abs(c)**2 / D + nvar # (b,) + Qc = Q.to(torch.cfloat) + A = torch.eye(D, device=DEV, dtype=torch.cfloat) + beta * c * Qc + S = (A @ A.mH / D).unsqueeze(0) \ + + rho.view(b, 1, 1) * torch.eye(D, device=DEV, + dtype=torch.cfloat) + x = torch.linalg.solve(S, t.unsqueeze(-1)) + return (A.mH.unsqueeze(0) @ x).squeeze(-1) / D -def att_apply(model, r, h1, h2): - M1, M2, Q1, Q2 = model - outs = [] - for u, (Mu, Qu, hu) in enumerate(((M1, Q1, h1), (M2, Q2, h2))): - xu = np.real(np.conj(hu) * (Mu.T @ r)) / (abs(hu)**2 + EPS_EQ) - sc = (Qu @ xu) / math.sqrt(D) - sc = sc - sc.max() - w = np.exp(sc); w /= w.sum() - outs.append(D * w * xu) - return outs +def abscos(a, b): + """a: (b,D) cfloat, b: (D,) float -> (b,) abs cosine.""" + num = (a * b.to(torch.cfloat).conj()).sum(1).abs() + return (num / (a.norm(dim=1) * b.norm())).cpu().numpy() # ------------------------------------------------------------------ -# ToDMA-adapted on real embeddings (complex channel) +# ToDMA-adapted on real embeddings (complex channel, numpy) # ------------------------------------------------------------------ def todma_prepare(V=1024, T=16): L = D // T @@ -238,58 +155,73 @@ def todma_run(tod, codes, h, sig, noise_slots): def main(): A, B, betas = load_pairs() npairs = len(A) - model = train_attention(float(betas.mean())) 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)] - print("[todma] sparse codes prepared") + print(f"[todma] sparse codes prepared; device = {DEV}") - keys = ("edma", "oma", "genie", "att", "att_x", "todma") - res = {k: np.zeros(len(SNRS)) for k in keys} - cnt = {k: np.zeros(len(SNRS)) for k in keys} + gen = torch.Generator(device=DEV).manual_seed(SEED) + nb = len(SNRS) + sigs_t = torch.tensor(10 ** (-SNRS / 20.0), device=DEV, + dtype=torch.float32) + keys = ("edma", "oma", "genie", "todma") + res = {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): - e1, e2, bi = A[i], B[i], float(betas[i]) - gi = 1.0 - bi**2 - c1, c2 = codes[i] + bi = float(betas[i]) + e1 = torch.tensor(A[i], dtype=torch.float32, device=DEV) + e2 = torch.tensor(B[i], dtype=torch.float32, device=DEV) + c1c, c2c = codes[i] for f in range(NFADE): - U1, U2 = haar(D), haar(D) - M1 = U1 - M2 = bi * U1 + math.sqrt(gi) * U2 - h = (rng.standard_normal(2) + 1j * rng.standard_normal(2)) \ + 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) - h1, h2 = h - r0 = h1 * (M1 @ e1) + h2 * (M2 @ e2) - n = (rng.standard_normal(D) + 1j * rng.standard_normal(D)) \ + n = (torch.randn(D, generator=gen, device=DEV) + + 1j * torch.randn(D, generator=gen, device=DEV)) \ / math.sqrt(2) - n2 = (rng.standard_normal(D) + 1j * rng.standard_normal(D)) \ + n2 = (torch.randn(D, generator=gen, device=DEV) + + 1j * torch.randn(D, generator=gen, device=DEV)) \ / math.sqrt(2) - nslots = [(rng.standard_normal(tod[4]) - + 1j * rng.standard_normal(tod[4])) / math.sqrt(2) - for _ in range(tod[3])] - # attention scheme transmits with ITS OWN trained masks - r0a = h1 * (model[0] @ e1) + h2 * (model[1] @ e2) - for k, s in enumerate(SNRS): - sig = 10 ** (-s / 20.0) - r = r0 + sig * n - t1 = M1.T @ r / h1; t2 = M2.T @ r / h2 - g1 = (t1 - bi * (h2 / h1) * t2) / gi - g2 = (t2 - bi * (h1 / h2) * t1) / gi - res["edma"][k] += 0.5 * (cosine(g1, e1) + cosine(g2, e2)) - o1 = e1 + math.sqrt(2) * sig * n / h1 - o2 = e2 + math.sqrt(2) * sig * n2 / h2 - res["oma"][k] += 0.5 * (cosine(o1, e1) + cosine(o2, e2)) - ge1 = M1.T @ (r - h2 * (M2 @ e2)) / h1 - ge2 = M2.T @ (r - h1 * (M1 @ e1)) / h2 - res["genie"][k] += 0.5 * (cosine(ge1, e1) + cosine(ge2, e2)) - a1, a2 = att_apply(model, r0a + sig * n, h1, h2) - res["att"][k] += 0.5 * (cosine(a1, e1) + cosine(a2, e2)) - res["att_x"][k] += 0.5 * (cosine(a1, e2) + cosine(a2, e1)) - for kk in ("edma", "oma", "genie", "att", "att_x"): - cnt[kk][k] += 1 - if f < 40: # ToDMA heavier: 40 fading draws - recs = todma_run(tod, (c1, c2), (h1, h2), sig, nslots) - got = [cosine(recs[j], (e1, e2)[j]) + r0 = h[0] * (M1 @ e1).to(torch.cfloat) \ + + h[1] * (M2 @ e2).to(torch.cfloat) + r = r0.unsqueeze(0) + sigs_t.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_t**2 / h[0].abs()**2 + v2 = sigs_t**2 / h[1].abs()**2 + g1 = aware_batch(t1, Q, bi, c1, v1) + g2 = aware_batch(t2, Q.T, bi, c2, v2) + res["edma"] += 0.5 * (abscos(g1, e1) + abscos(g2, e2)) + o1 = e1.to(torch.cfloat).unsqueeze(0) \ + + math.sqrt(2) * sigs_t.view(-1, 1) * n.unsqueeze(0) / h[0] + o2 = e2.to(torch.cfloat).unsqueeze(0) \ + + math.sqrt(2) * sigs_t.view(-1, 1) * n2.unsqueeze(0) / h[1] + res["oma"] += 0.5 * (abscos(o1, e1) + abscos(o2, e2)) + 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] + res["genie"] += 0.5 * (abscos(ge1, e1) + abscos(ge2, e2)) + for kk in ("edma", "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])] + e1n, e2n = A[i], B[i] + for k, s in enumerate(SNRS): + sig = 10 ** (-s / 20.0) + recs = todma_run(tod, (c1c, c2c), hnp, sig, nslots) + got = [cosine(recs[j], (e1n, e2n)[j]) for j in range(2) if recs[j] is not None] if got: res["todma"][k] += float(np.mean(got)) @@ -299,23 +231,6 @@ def main(): for k in keys: res[k] /= np.maximum(cnt[k], 1) - fig, ax = plt.subplots() - ax.plot(SNRS, res["edma"], "o-", color="C3", label="EDMA (closed form)") - ax.plot(SNRS, res["att"], "s--", color="C0", - label="Attention-based (retrained)") - ax.plot(SNRS, res["todma"], "d-.", color="C4", label="ToDMA-adapted") - ax.plot(SNRS, res["oma"], "v:", color="C1", label="OMA") - ax.plot(SNRS, res["genie"], "-", color="gray", lw=1.0, - label="Genie-aided SIC bound") - ax.set_xlabel("Per-block SNR $\\rho$ [dB]") - ax.set_ylabel("Mean cosine similarity") - ax.set_xlim(SNRS[0], SNRS[-1]); ax.set_ylim(0, 0.85) - ax.legend(loc="upper left") - fig.subplots_adjust(**AXES_RECT) - fig.savefig(FIG / "fig_bertvit_merged.pdf") - plt.close(fig) - print(f"[OK] wrote {FIG/'fig_bertvit_merged.pdf'}") - with open(DATA / "bertvit_merged.csv", "w", newline="") as fcsv: w = csv.writer(fcsv) w.writerow(["snr_db"] + list(keys)) @@ -323,8 +238,7 @@ def main(): w.writerow([s] + [res[key][k] for key in keys]) print(f"[OK] wrote {DATA/'bertvit_merged.csv'}") for k, s in enumerate(SNRS): - print(f" {s:4.0f} dB EDMA {res['edma'][k]:.3f} " - f"ATT {res['att'][k]:.3f} (x {res['att_x'][k]:.3f}) " + print(f" {s:4.1f} dB EDMA {res['edma'][k]:.3f} " f"ToDMA {res['todma'][k]:.3f} OMA {res['oma'][k]:.3f} " f"genie {res['genie'][k]:.3f}") diff --git a/code/refine_matched.py b/code/refine_matched.py index 0c0a852..81e48b5 100644 --- a/code/refine_matched.py +++ b/code/refine_matched.py @@ -1,23 +1,23 @@ """ -Capacity-matched EDMA refinement (parameter budget equal to the -attention scheme: 4 d^2 = 2.36M at d = 768). +Refinement stage for the v2 (affinity-aware Wiener) EDMA receiver. ================================================================ -Four-head averaged gated refinement applied to the closed-form -demultiplexer output: +Trains the single-gate refinement operator - out = (1/4) sum_k D softmax(Q_k x / sqrt(D)) .* x, + out = D softmax(W z / sqrt(D)) .* z, z = Re(e_hat), -with Q_1..Q_4 in R^{D x D} (4 d^2 parameters, exactly the -attention scheme's budget). The single-gate 0.59M refiner is the -special case of four identical heads, so the family contains it -by construction. Same training recipe: demux outputs from -parametric pairs at beta = 0.028, Haar pool 32, Rayleigh -channels, complex noise, training SNR uniform in [5, 25] dB, -Adam 5e-4 with gradient clipping, batch 48, 200 epochs. +(0.59M parameters at d = 768) on aware-demultiplexer outputs, then +warm-starts a capacity-check variant with four heads (4 d^2 = 2.36M) +and fine-tunes it, so the family contains the single gate by +construction. Training data: parametric pairs at beta = 0.028, a +fixed pool of 32 independent Haar mask pairs, Rayleigh channels, +complex noise, training SNR uniform in [5, 25] dB, Adam 5e-4 with +gradient clipping, batch 48, 220 epochs (stage 2 from epoch 120 at +lr 2e-4). Evaluation on the real BERT/ViT pairs with fresh Haar masks and -200 fading draws per pair. Appends column `edma_ref2` to -data/bertvit_merged.csv and prints all-curve numbers. +NFADE fading draws per pair. Appends columns `edma_ref` (single +gate) and `edma_ref2` (four heads) to data/bertvit_merged.csv. +Requires torch (run under WSL with CUDA if available). """ from __future__ import annotations import csv @@ -26,161 +26,182 @@ import time import numpy as np import torch -from fig_real_merged import load_pairs, cosine, SNRS, NFADE, D, DATA +from fig_real_merged import load_pairs, SNRS, NFADE, D, DATA SEED = 2026 -rng = np.random.default_rng(SEED + 31) torch.manual_seed(SEED + 31) +DEV = "cuda" if torch.cuda.is_available() else "cpu" BETA0 = 0.028 G0 = 1.0 - BETA0**2 +print(f"[refine] device = {DEV}") -def haar_t(gen): - Q, R = torch.linalg.qr(torch.randn(D, D, generator=gen)) - return Q * torch.sign(torch.diagonal(R)) +def haar_t(n, gen): + G = torch.randn(n, D, D, generator=gen, device=DEV) + Q, R = torch.linalg.qr(G) + return Q * torch.sign(torch.diagonal(R, dim1=-2, dim2=-1)).unsqueeze(-2) -def train_refiner2(epochs=220, steps=20, batch=48, lr=5e-4, - l2=0.5, l3=0.5, pool=32): - """Stage 1 trains a single gate (the proven 0.59M recipe); stage 2 - warm-starts four heads from it plus small perturbations and - fine-tunes at a reduced learning rate, so the capacity-matched - family starts at the single-gate solution it contains.""" - print(f"=== training capacity-matched refinement (4-head gate, " - f"4d^2 = {4*D*D/1e6:.2f}M params, warm-started) ===", - flush=True) - gen = torch.Generator().manual_seed(SEED + 31) - masks = [] - for _ in range(pool): - U1, U2 = haar_t(gen), haar_t(gen) - masks.append((U1.numpy(), (BETA0 * U1 - + math.sqrt(G0) * U2).numpy())) - Q0 = torch.nn.Parameter(torch.randn(D, D, generator=gen) - / math.sqrt(D)) - params = [Q0] +def aware_t(t1, Q, beta, c1, nvar): + """Batched affinity-aware Wiener demux in torch (complex).""" + b = t1.shape[0] + g = 1.0 - beta * beta + rho = g * (c1.abs()**2) / D + nvar # (b,) + A = torch.eye(D, device=DEV, dtype=torch.cfloat).expand(b, D, D) \ + + beta * c1.view(b, 1, 1) * Q.to(torch.cfloat) + S = A @ A.mH / D + rho.view(b, 1, 1) \ + * torch.eye(D, device=DEV, dtype=torch.cfloat) + x = torch.linalg.solve(S, t1.unsqueeze(-1)) + return (A.mH @ x).squeeze(-1) / D + + +def train_batch(masks, Qs, gen, batch): + """Generate one training batch of aware-demux outputs (user 1).""" + e1 = torch.nn.functional.normalize( + torch.randn(batch, D, generator=gen, device=DEV), dim=1) + w = torch.randn(batch, D, generator=gen, device=DEV) + w = w - (w * e1).sum(1, keepdim=True) * e1 + w = torch.nn.functional.normalize(w, dim=1) + e2 = BETA0 * e1 + math.sqrt(G0) * w + sel = torch.randint(len(masks), (batch,), generator=gen, device=DEV) + M1 = masks[0][sel]; M2 = masks[1][sel]; Q = Qs[sel] + snr = 5.0 + 20.0 * torch.rand(batch, generator=gen, device=DEV) + sig = 10 ** (-snr / 20.0) + h = (torch.randn(batch, 2, generator=gen, device=DEV) + + 1j * torch.randn(batch, 2, generator=gen, device=DEV)) \ + / math.sqrt(2) + n = (torch.randn(batch, D, generator=gen, device=DEV) + + 1j * torch.randn(batch, D, generator=gen, device=DEV)) \ + / math.sqrt(2) + r = h[:, :1] * (M1 @ e1.unsqueeze(-1)).squeeze(-1).to(torch.cfloat) \ + + h[:, 1:2] * (M2 @ e2.unsqueeze(-1)).squeeze(-1).to(torch.cfloat) \ + + sig.view(-1, 1) * n + t1 = (M1.transpose(-1, -2).to(torch.cfloat) + @ r.unsqueeze(-1)).squeeze(-1) / h[:, :1] + c1 = h[:, 1] / h[:, 0] + nvar = sig**2 / h[:, 0].abs()**2 + g1 = aware_t(t1, Q, BETA0, c1, nvar) + return g1.real.float(), e1 + + +def train_refiners(epochs=220, steps=20, batch=48, lr=5e-4, + l2=0.5, l3=0.5, pool=32, stage2_at=120): + print(f"=== training refinement (single gate {D*D/1e6:.2f}M, " + f"then 4-head warm start {4*D*D/1e6:.2f}M) ===", flush=True) + gen = torch.Generator(device=DEV).manual_seed(SEED + 31) + U1 = haar_t(pool, gen); U2 = haar_t(pool, gen) + masks = (U1, U2) + Qs = U1.transpose(-1, -2) @ U2 + params = [torch.nn.Parameter( + torch.randn(D, D, generator=gen, device=DEV) / math.sqrt(D))] opt = torch.optim.Adam(params, lr=lr) - stage2_at = 120 # epochs of single-gate pre-training + P_single = None - def forward(x): + def forward(x, ps): outs = [D * torch.softmax((x @ Qk.T) / math.sqrt(D), dim=1) * x - for Qk in params] - return sum(outs) / len(params) + for Qk in ps] + return sum(outs) / len(ps) t0 = time.time() for ep in range(epochs): if ep == stage2_at: + P_single = params[0].detach().clone() base = params[0].detach() params = [torch.nn.Parameter( - base.clone() + 0.02 * torch.randn(D, D, generator=gen) + base.clone() + 0.02 * torch.randn(D, D, generator=gen, + device=DEV) / math.sqrt(D)) for _ in range(4)] opt = torch.optim.Adam(params, lr=2e-4) - print(f" [warm start] 4 heads initialised from the trained " - f"gate at epoch {ep}", flush=True) + print(f" [warm start] 4 heads at epoch {ep}", flush=True) for _ in range(steps): - xs, ts = [], [] - for _ in range(batch): - e1 = torch.nn.functional.normalize( - torch.randn(D, generator=gen), dim=0).numpy() - w = torch.randn(D, generator=gen).numpy() - w = w - (w @ e1) * e1 - w = w / np.linalg.norm(w) - e2 = BETA0 * e1 + math.sqrt(G0) * w - M1, M2 = masks[int(torch.randint(pool, (1,), - generator=gen))] - snr = float(5.0 + 20.0 * torch.rand(1, generator=gen)) - sig = 10 ** (-snr / 20.0) - h = (torch.randn(2, generator=gen).numpy() - + 1j * torch.randn(2, generator=gen).numpy()) \ - / math.sqrt(2) - nc = (torch.randn(D, generator=gen).numpy() - + 1j * torch.randn(D, generator=gen).numpy()) \ - / math.sqrt(2) - rc = h[0] * (M1 @ e1) + h[1] * (M2 @ e2) + sig * nc - t1 = M1.T @ rc / h[0] - t2 = M2.T @ rc / h[1] - g1 = (t1 - BETA0 * (h[1] / h[0]) * t2) / G0 - xs.append(torch.tensor(np.real(g1), dtype=torch.float32)) - ts.append(torch.tensor(e1, dtype=torch.float32)) - x = torch.stack(xs); t = torch.stack(ts) - out = forward(x) - mse = ((out - t)**2).mean() - cs = torch.nn.functional.cosine_similarity(out, t, dim=1).mean() + with torch.no_grad(): + x, tgt = train_batch(masks, Qs, gen, batch) + out = forward(x, params) + mse = ((out - tgt)**2).mean() + cs = torch.nn.functional.cosine_similarity(out, tgt, dim=1).mean() loss = l2 * mse + l3 * (1.0 - cs) opt.zero_grad(); loss.backward() torch.nn.utils.clip_grad_norm_(params, 1.0) opt.step() - if (ep + 1) % 50 == 0: + if (ep + 1) % 40 == 0: 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.detach().numpy() for p in params] + return P_single, [p.detach() for p in params] -def refine2(P, g): - x = np.real(g) - - def gate(Q, v): - sc = (Q @ v) / math.sqrt(D) - sc = sc - sc.max() - w = np.exp(sc); w /= w.sum() - return D * w * v - - return sum(gate(Qk, x) for Qk in P) / 4.0 +def refine_apply(ps, z): + """z: (b, D) real torch tensor; ps: list of gates.""" + outs = [D * torch.softmax((z @ Qk.T) / math.sqrt(D), dim=1) * z + for Qk in ps] + return sum(outs) / len(ps) def main(): A, B, betas = load_pairs() - P = train_refiner2() - ref = np.zeros(len(SNRS)); cnt = 0 + P1, P4 = train_refiners() + gen = torch.Generator(device=DEV).manual_seed(SEED + 77) + ref1 = np.zeros(len(SNRS)); ref4 = np.zeros(len(SNRS)); cnt = 0 t0 = time.time() + At = torch.tensor(A, dtype=torch.float32, device=DEV) + Bt = torch.tensor(B, dtype=torch.float32, device=DEV) for i in range(len(A)): - e1, e2, bi = A[i], B[i], float(betas[i]) - gi = 1.0 - bi**2 + bi = float(betas[i]) + e1 = At[i]; e2 = Bt[i] for f in range(NFADE): - G1 = rng.standard_normal((D, D)) - Qh, Rh = np.linalg.qr(G1) - U1 = Qh * np.sign(np.diag(Rh)) - G2 = rng.standard_normal((D, D)) - Qh, Rh = np.linalg.qr(G2) - U2 = Qh * np.sign(np.diag(Rh)) - M1 = U1 - M2 = bi * U1 + math.sqrt(gi) * U2 - h = (rng.standard_normal(2) + 1j * rng.standard_normal(2)) \ + 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) - h1, h2 = h - r0 = h1 * (M1 @ e1) + h2 * (M2 @ e2) - n = (rng.standard_normal(D) + 1j * rng.standard_normal(D)) \ + n = (torch.randn(D, generator=gen, device=DEV) + + 1j * torch.randn(D, generator=gen, device=DEV)) \ / math.sqrt(2) - for k, s in enumerate(SNRS): - sig = 10 ** (-s / 20.0) - r = r0 + sig * n - t1 = M1.T @ r / h1; t2 = M2.T @ r / h2 - g1 = (t1 - bi * (h2 / h1) * t2) / gi - g2 = (t2 - bi * (h1 / h2) * t1) / gi - ref[k] += 0.5 * (cosine(refine2(P, g1), e1) - + cosine(refine2(P, g2), e2)) + r0 = h[0] * (M1 @ e1).to(torch.cfloat) \ + + h[1] * (M2 @ e2).to(torch.cfloat) + sigs = torch.tensor(10 ** (-SNRS / 20.0), device=DEV, + dtype=torch.float32) + nb = len(SNRS) + 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]).expand(nb) + c2 = (h[0] / h[1]).expand(nb) + v1 = (sigs**2 / h[0].abs()**2) + v2 = (sigs**2 / h[1].abs()**2) + g1 = aware_t(t1, Q.expand(nb, D, D), bi, c1, v1).real.float() + g2 = aware_t(t2, Q.T.expand(nb, D, D), bi, c2, v2).real.float() + with torch.no_grad(): + for P, acc in ((([P1]), ref1), ((P4), ref4)): + o1 = refine_apply(P, g1) + o2 = refine_apply(P, g2) + cs1 = torch.nn.functional.cosine_similarity( + o1, e1.unsqueeze(0), dim=1).abs() + cs2 = torch.nn.functional.cosine_similarity( + o2, e2.unsqueeze(0), dim=1).abs() + acc += (0.5 * (cs1 + cs2)).cpu().numpy() cnt += 1 print(f" pair {i+1}/{len(A)} done ({time.time()-t0:.0f}s)", flush=True) - ref /= cnt + ref1 /= cnt; ref4 /= cnt rows = list(csv.DictReader(open(DATA / "bertvit_merged.csv"))) names = list(rows[0].keys()) - if "edma_ref2" not in names: - names.append("edma_ref2") + for col in ("edma_ref", "edma_ref2"): + if col not in names: + names.append(col) for k, r in enumerate(rows): - r["edma_ref2"] = f"{ref[k]}" + r["edma_ref"] = f"{ref1[k]}" + r["edma_ref2"] = f"{ref4[k]}" with open(DATA / "bertvit_merged.csv", "w", newline="") as f: w = csv.DictWriter(f, fieldnames=names) w.writeheader(); w.writerows(rows) - print("[OK] appended edma_ref2 to bertvit_merged.csv") + print("[OK] appended edma_ref / edma_ref2 to bertvit_merged.csv") for k, r in enumerate(rows): - print(f" {float(r['snr_db']):4.0f} dB " - f"EDMA {float(r['edma']):.3f} " - f"ref(0.59M) {float(r['edma_ref']):.3f} " - f"ref2(2.36M) {ref[k]:.3f} " - f"ATT(2.36M) {float(r['att']):.3f} " - f"genie {float(r['genie']):.3f}") + print(f" {float(r['snr_db']):4.1f} dB " + f"EDMA {float(r['edma']):.3f} ref {ref1[k]:.3f} " + f"ref2 {ref4[k]:.3f} genie {float(r['genie']):.3f}") if __name__ == "__main__": diff --git a/code/replot_all.py b/code/replot_all.py new file mode 100644 index 0000000..83d314d --- /dev/null +++ b/code/replot_all.py @@ -0,0 +1,179 @@ +"""Canonical figure rendering. Reads ONLY data/*.csv, writes fig/*.pdf. + +Figures: fig_floor, fig_rate_corrected, fig_beta_sweep_corrected, +fig_sic, fig_multiuser_corrected. (fig_bertvit_merged is rendered by +replot_merged.py; block_diagram.pdf comes from block_diagram_src.tex.) +One physical geometry and one label dictionary for every plot. +""" +import csv +import math +from pathlib import Path +import matplotlib +matplotlib.use("Agg") +import matplotlib.pyplot as plt + +ROOT = Path(__file__).resolve().parents[1] +DATA = ROOT / "data" +FIG = ROOT / "fig" + +plt.rcParams.update({ + "font.family": "serif", + "font.serif": ["DejaVu Serif", "Times New Roman"], + "font.size": 9, "axes.labelsize": 9, "legend.fontsize": 6.6, + "xtick.labelsize": 8, "ytick.labelsize": 8, + "axes.grid": True, "grid.linestyle": "--", "grid.linewidth": 0.4, + "grid.alpha": 0.6, "lines.linewidth": 1.4, "lines.markersize": 4.0, + "figure.figsize": (3.15, 2.36), "pdf.fonttype": 42, +}) +AXES_RECT = dict(left=0.205, right=0.965, top=0.955, bottom=0.185) + +LBL = { + "edma": "EDMA", + "blind": "Affinity-blind", + "oma": "OMA", + "genie": "Genie-aided SIC bound", + "sic": "Realizable analog SIC", + "todma": "ToDMA-adapted", + "mac": "MAC sum capacity", + "coop": "Full-cooperation bound", + "hybrid": "EDMA + refinement stage", +} + + +def rows_of(name): + return list(csv.DictReader(open(DATA / f"{name}.csv"))) + + +def col(rows, k): + return [float(r[k]) for r in rows] + + +def save(fig, name): + fig.subplots_adjust(**AXES_RECT) + fig.savefig(FIG / f"{name}.pdf") + plt.close(fig) + print(f"[OK] wrote {name}.pdf") + + +# ------------------------------------------------------ fig_floor +def fig_floor(): + rows = rows_of("floor_validation") + fig, ax = plt.subplots() + colors = {"256": "C0", "768": "C3"} + beta = 0.311 + for d in ("256", "768"): + rd = [r for r in rows if r["d"] == d or r["d"] == f"{d}.0" + 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}$") + if d == "768": + ax.plot(snr, col(rd, "mse_blind"), "--", color="C1", lw=1.2, + label=LBL["blind"]) + 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=":") + ax.annotate("blind floor $1/2$", xy=(17.0, 0.512), fontsize=7, + color="gray") + ax.annotate(r"aware floor $\sqrt{1-\beta^2}/2$", xy=(14.0, 0.432), + 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.05) + ax.legend(loc="lower left", bbox_to_anchor=(0.02, 0.18)) + save(fig, "fig_floor") + + +# ------------------------------------------------ fig_rate_corrected +def fig_rate(): + rows = rows_of("rate_corrected") + snr = col(rows, "snr_db") + fig, ax = plt.subplots() + ax.plot(snr, col(rows, "edma"), "-", color="C3", label=LBL["edma"]) + ax.plot(snr, col(rows, "blind"), ":", color="C4", lw=1.2, + label=LBL["blind"]) + ax.plot(snr, col(rows, "oma"), "--", color="C1", label=LBL["oma"]) + ax.plot(snr, col(rows, "genie"), "-.", color="C0", label=LBL["genie"]) + ax.plot(snr, col(rows, "mac"), "-", color="k", lw=1.0, label=LBL["mac"]) + ax.set_xlabel("Per-block SNR $\\rho$ [dB]") + ax.set_ylabel("Effective sum rate [bps/Hz]") + ax.set_xlim(0, 40); ax.set_ylim(0, 3.2) + ax.legend(loc="upper left") + save(fig, "fig_rate_corrected") + + +# ------------------------------------------ fig_beta_sweep_corrected +def fig_beta_sweep(): + rows = rows_of("beta_sweep_corrected") + fig, ax = plt.subplots() + for s, cc in (("10", "C0"), ("20", "C3")): + rd = [r for r in rows if float(r["snr_db"]) == float(s)] + b = col(rd, "beta") + ax.plot(b, col(rd, "edma"), "-", color=cc, + label=rf"EDMA, $\rho={s}$ dB") + ax.axhline(float(rd[0]["blind"]), color=cc, ls=":", lw=1.0) + ax.axhline(float(rd[0]["oma"]), color=cc, ls="--", lw=1.0) + ax.axhline(float(rd[0]["genie"]), color=cc, ls="-.", lw=0.8) + # one legend entry per reference style (color-independent) + ax.plot([], [], ls=":", color="gray", label=LBL["blind"]) + 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.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) + ax.legend(loc="upper left") + save(fig, "fig_beta_sweep_corrected") + + +# ------------------------------------------------------- fig_sic +def fig_sic(): + rows = rows_of("sic_comparison") + 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, "blind"), "d:", color="C4", label=LBL["blind"]) + ax.plot(snr, col(rows, "sic"), "^-.", color="C2", label=LBL["sic"]) + 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.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.7) + ax.legend(loc="upper left") + save(fig, "fig_sic") + + +# ------------------------------------------ fig_multiuser_corrected +def fig_multiuser(): + rows = rows_of("multiuser_corrected") + fig, ax = plt.subplots() + colors = {"2": "C0", "3": "C2", "4": "C3"} + for U in ("2", "3", "4"): + rd = [r for r in rows if float(r["U"]) == float(U)] + snr = col(rd, "snr_db") + ax.plot(snr, col(rd, "edma_mc"), "-", color=colors[U], + label=rf"EDMA, $U={U}$") + ax.plot(snr, col(rd, "oma"), "--", color=colors[U], lw=1.0, + label=rf"OMA, $U={U}$") + mk = [i for i, s in enumerate(snr) if s % 5 == 0] + ax.plot([snr[i] for i in mk], [col(rd, "edma_mc")[i] for i in mk], + "o", color=colors[U], ms=4, mfc="none") + ax.set_xlabel("Per-block SNR $\\rho$ [dB]") + ax.set_ylabel("Effective sum rate [bps/Hz]") + ax.set_xlim(0, 30) + ax.legend(loc="upper left") + save(fig, "fig_multiuser_corrected") + + +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} + for name, fn in ALL.items(): + if not todo or name in todo: + fn() diff --git a/code/replot_merged.py b/code/replot_merged.py index d3d200b..f5ae413 100644 --- a/code/replot_merged.py +++ b/code/replot_merged.py @@ -1,6 +1,7 @@ """Canonical replot of fig_bertvit_merged.pdf from data/bertvit_merged.csv. Curves: EDMA, EDMA + refinement (hybrid), ToDMA-adapted, OMA, genie bound. -The attention columns remain in the CSV but are not plotted.""" +The capacity-check column edma_ref2 remains in the CSV but is not +plotted (it tracks edma_ref; quoted in the text only).""" import csv from pathlib import Path import matplotlib @@ -24,7 +25,7 @@ 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 (closed form)") +ax.plot(snr, col("edma"), "o-", color="C3", label="EDMA") ax.plot(snr, col("edma_ref"), "^-", color="C2", label="EDMA + refinement stage") ax.plot(snr, col("todma"), "d-.", color="C4", label="ToDMA-adapted") diff --git a/code/replot_sic.py b/code/replot_sic.py deleted file mode 100644 index a7d633f..0000000 --- a/code/replot_sic.py +++ /dev/null @@ -1,43 +0,0 @@ -"""Canonical replot of fig_sic.pdf from data/sic_comparison.csv -(realizable analog SIC vs genie SIC vs EDMA vs OMA, beta = 0.311, -d = 512, block-Rayleigh). US-spelling labels, uniform geometry.""" -import csv -from pathlib import Path -import matplotlib -matplotlib.use("Agg") -import matplotlib.pyplot as plt - -ROOT = Path(__file__).resolve().parents[1] -plt.rcParams.update({ - "font.family": "serif", - "font.serif": ["DejaVu Serif", "Times New Roman"], - "font.size": 9, "axes.labelsize": 9, "legend.fontsize": 6.6, - "xtick.labelsize": 8, "ytick.labelsize": 8, - "axes.grid": True, "grid.linestyle": "--", "grid.linewidth": 0.4, - "grid.alpha": 0.6, "lines.linewidth": 1.4, "lines.markersize": 4.0, - "figure.figsize": (3.15, 2.36), "pdf.fonttype": 42, -}) -AXES_RECT = dict(left=0.205, right=0.965, top=0.955, bottom=0.185) - -rows = list(csv.DictReader(open(ROOT / "data" / "sic_comparison.csv"))) -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 (closed form)") -ax.plot(snr, col("sic"), "^-.", color="C2", label="Realizable analog SIC") -ax.plot(snr, col("oma"), "v:", color="C1", label="OMA") -ax.plot(snr, col("genie"), "-", color="gray", lw=1.0, - label="Genie-aided SIC bound") -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.7) -ax.legend(loc="upper left") -fig.subplots_adjust(**AXES_RECT) -fig.savefig(ROOT / "fig" / "fig_sic.pdf") -print("[OK] wrote fig_sic.pdf") -for r in rows: - print(f" {float(r['snr_db']):4.0f} dB EDMA {float(r['edma']):.3f} " - f"SIC {float(r['sic']):.3f} genie {float(r['genie']):.3f} " - f"OMA {float(r['oma']):.3f}") diff --git a/code/revision_sims.py b/code/revision_sims.py index c0ab0e6..6c48415 100644 --- a/code/revision_sims.py +++ b/code/revision_sims.py @@ -1,26 +1,45 @@ """ -Revision simulations for the EDMA TCOM resubmission. +Simulations for the EDMA TVT manuscript (v2 design). ======================================================= -Implements the per-realisation (finite-d) analysis and the corrected -energy-normalised rate accounting, plus the reviewer-requested -experiments: +Design v2: each user applies an independent orthogonal mask; the +receiver runs one matched filter per user followed by the +affinity-aware linear MMSE demultiplexer, which exploits the +coherent interference component that the pairwise affinity beta +predicts. Per-realization statistic for user 1 (c1 = h2/h1): - E0 Theorem-1 verification: exact self-interference constant C_SI - E1 fig_floor : per-user MSE vs block SNR, interference floor - E2 fig_sic : realisable SIC vs genie SIC vs EDMA vs OMA - E3 (text numbers) : Rayleigh unconditional MSE, ZF vs regularised - E4 fig_csi : imperfect-CSI robustness - E5 fig_maskfam : Walsh-Hadamard structured masks vs Haar - E6 fig_coop : high-affinity combining-mode crossover - E7 fig_rate_corrected, fig_beta_sweep_corrected, fig_multiuser_corrected + t1 = (I + beta*c1*Q) e1 + sqrt(g)*c1*Q w + n_t, Q = M1^T M2, -Conventions (identical to the revised manuscript): +and the demultiplexer is the Wiener filter + + e1_hat = (1/d) A^H (A A^H/d + (g|c1|^2/d + sig^2/|h1|^2) I)^{-1} t1, + A = I + beta*c1*Q, g = 1 - beta^2. + +Closed form (Theorem 1, d -> inf, per channel realization): + + MSE_1 = rho_e / sqrt((1 + beta^2|c1|^2 + rho_e)^2 - 4 beta^2|c1|^2), + rho_e = g|c1|^2 + d sig^2/|h1|^2; floor at |c1| = 1: sqrt(g)/2. + +The affinity-blind receiver (beta = 0 in the filter) reduces to a +scalar shrinkage of the matched filter with floor 1/2, so the entire +cosine gain of the aware receiver is attributable to the predicted +affinity. Effective SINR: eta = 1/MSE - 1 (biased MMSE convention). + +Experiments in this file (CPU, numpy): + E0 theorem_check : closed form vs Monte Carlo, both users + E1 fig_floor : per-user MSE vs block SNR, aware vs blind floor + E7a rate_corrected + beta_sweep_corrected : closed-form rate curves + +The Monte Carlo experiments E2, E3, E4, E5, E7c, E8, E9 are canonical +in revision_sims_gpu.py (torch backend, run under WSL); figures are +rendered from data/ by replot_all.py and replot_merged.py. + +Conventions (identical to the manuscript): * unit per-block transmit energy E_b = 1 per user * rho = E_b / sigma_n^2 (per-block received SNR; per-symbol SNR rho/d) * block-Rayleigh h ~ CN(0,1) unless the AWGN point |h|=1 is stated * complex AWGN CN(0, sigma^2 I_d); embeddings real, unit norm * orientation convention = +beta -Fixed seed. CSVs -> ../fig, PDFs -> ../fig_toc. +Fixed seed 2026. CSVs -> ../data, PDFs -> ../fig. """ from __future__ import annotations import csv @@ -39,13 +58,27 @@ plt.rcParams.update({ "font.family": "serif", "font.serif": ["DejaVu Serif", "Times New Roman"], "font.size": 9, "axes.labelsize": 9, "axes.titlesize": 9, - "legend.fontsize": 7.0, "xtick.labelsize": 8, "ytick.labelsize": 8, + "legend.fontsize": 6.6, "xtick.labelsize": 8, "ytick.labelsize": 8, "axes.grid": True, "grid.linestyle": "--", "grid.linewidth": 0.4, "grid.alpha": 0.6, "lines.linewidth": 1.4, "lines.markersize": 4.0, "figure.figsize": (3.15, 2.36), "pdf.fonttype": 42, }) AXES_RECT = dict(left=0.205, right=0.965, top=0.955, bottom=0.185) +# shared legend-label dictionary (single source for every figure) +LBL = { + "edma": "EDMA", + "blind": "Affinity-blind", + "oma": "OMA", + "genie": "Genie-aided SIC bound", + "sic": "Realizable analog SIC", + "todma": "ToDMA-adapted", + "mac": "MAC sum capacity", + "hybrid": "EDMA + refinement stage", + "haar": "Haar masks", + "wh": "Walsh-Hadamard masks", +} + rng = np.random.default_rng(2026) @@ -86,69 +119,80 @@ def embed_pair(d, beta): return e1, e2 -def two_user_masks(d, beta, U1=None, U2=None): - if U1 is None: U1 = haar(d) - if U2 is None: U2 = haar(d) - g = math.sqrt(1.0 - beta**2) - return U1, beta * U1 + g * U2 - - def rayleigh(n=1): return (rng.standard_normal(n) + 1j * rng.standard_normal(n)) / math.sqrt(2) -def C_SI(beta, c): - """User-1 self-interference constant (exact to O(1/d)), =+beta.""" - g = 1.0 - beta**2 - return (g**2 * abs(c)**2 + beta**2 + beta**4 * abs(c)**2 - + 2.0 * beta**4 * np.real(c)) / g - - -def C_SI2(beta, c2): - """User-2 self-interference constant (deterministic), c2 = h1/h2.""" - g = 1.0 - beta**2 - return (abs(c2)**2 + beta**2 + 2.0 * beta**2 * np.real(c2)) / g - - -def C_bar(beta): - """Symmetrised constant at |h|=1 (block-alternating mask roles).""" - return 0.5 * (C_SI(beta, 1.0 + 0j) + C_SI2(beta, 1.0 + 0j)) - - -def demux(r, M1, M2, h1, h2, beta): - """beta-aware demultiplexer (13); returns (e1_hat, e2_hat).""" - g = 1.0 - beta**2 - t1 = (M1.T @ r) / h1 - t2 = (M2.T @ r) / h2 - e1 = (t1 - beta * (h2 / h1) * t2) / g - e2 = (t2 - beta * (h1 / h2) * t1) / g - return e1, e2 +def cnoise(d): + return (rng.standard_normal(d) + 1j * rng.standard_normal(d)) / math.sqrt(2) def cosine(a, b): return abs(np.vdot(a, b)) / (np.linalg.norm(a) * np.linalg.norm(b)) +def mse_theory(beta, c1, rho_e): + """Theorem 1: per-realization MSE of the aware demultiplexer.""" + a0 = 1.0 + beta**2 * abs(c1)**2 + rho_e + return rho_e / math.sqrt(a0 * a0 - 4.0 * beta**2 * abs(c1)**2) + + +def mse_blind(c1, dsig2_h): + """Affinity-blind scalar-shrinkage MSE (beta = 0 in the filter).""" + r0 = abs(c1)**2 + dsig2_h + return r0 / (1.0 + r0) + + +def eta_of(mse): + """Effective SINR of a (possibly biased) estimator with unit signal.""" + return 1.0 / mse - 1.0 + + +def aware(t1, Q, beta, c1, nvar, d): + """Affinity-aware Wiener demultiplexer applied to t1 = M1^T r / h1. + + Uses A A^H = (1+beta^2|c1|^2) I + beta(c1 Q + conj(c1) Q^T), so the + system matrix is assembled in O(d^2) and solved with one LU.""" + g = 1.0 - beta * beta + rho = g * abs(c1)**2 / d + nvar + S = beta * (c1 * Q + np.conj(c1) * Q.T) / d + S[np.diag_indices(d)] += (1.0 + beta**2 * abs(c1)**2) / d + rho + x = np.linalg.solve(S, t1) + return (x + beta * np.conj(c1) * (Q.T @ x)) / d + + +def blind(t1, c1, nvar, d): + """Affinity-blind receiver: scalar shrinkage of the matched filter.""" + lam = (1.0 / d) / (1.0 / d + abs(c1)**2 / d + nvar) + return lam * t1 + + # ------------------------------------------------------------------ -# E0 : Theorem-1 verification +# E0 : Theorem-1 verification (both users, random phases) # ------------------------------------------------------------------ -def E0_theorem_check(d=512, betas=(0.0, 0.311, 0.5, 0.7), ntr=300): - print("\n=== E0: Theorem 1 (self-interference constant) verification ===") +def E0_theorem_check(d=512, betas=(0.0, 0.311, 0.5, 0.7), ntr=200, snr=20.0): + print("\n=== E0: Theorem 1 (aware-demultiplexer MSE) verification ===") + sig = 10 ** (-snr / 20.0) rows = [] worst = 0.0 for beta in betas: - # random unit-modulus channels (AWGN-type magnitude, random phase) - errs1, errs2 = [], [] + g = 1.0 - beta**2 + r1s, r2s = [], [] for _ in range(ntr): h1 = np.exp(1j * rng.uniform(0, 2 * np.pi)) h2 = np.exp(1j * rng.uniform(0, 2 * np.pi)) e1, e2 = embed_pair(d, beta) - M1, M2 = two_user_masks(d, beta) - r = h1 * (M1 @ e1) + h2 * (M2 @ e2) # noise-free - g1, g2 = demux(r, M1, M2, h1, h2, beta) - errs1.append(np.linalg.norm(g1 - e1)**2 / C_SI(beta, h2 / h1)) - errs2.append(np.linalg.norm(g2 - e2)**2 / C_SI2(beta, h1 / h2)) - r1, r2 = float(np.mean(errs1)), float(np.mean(errs2)) + M1, M2 = haar(d), haar(d) + Q = M1.T @ M2 + r = h1 * (M1 @ e1) + h2 * (M2 @ e2) + sig * cnoise(d) + c1, c2 = h2 / h1, h1 / h2 + g1 = aware(M1.T @ r / h1, Q, beta, c1, sig**2 / abs(h1)**2, d) + g2 = aware(M2.T @ r / h2, Q.T, beta, c2, sig**2 / abs(h2)**2, d) + th1 = mse_theory(beta, c1, g * abs(c1)**2 + d * sig**2 / abs(h1)**2) + th2 = mse_theory(beta, c2, g * abs(c2)**2 + d * sig**2 / abs(h2)**2) + r1s.append(np.linalg.norm(g1 - e1)**2 / th1) + r2s.append(np.linalg.norm(g2 - e2)**2 / th2) + r1, r2 = float(np.mean(r1s)), float(np.mean(r2s)) dev = max(abs(r1 - 1.0), abs(r2 - 1.0)) * 100 worst = max(worst, dev) print(f" beta={beta:.3f} MC/theory user1 = {r1:.4f}, user2 = {r2:.4f}" @@ -161,12 +205,11 @@ def E0_theorem_check(d=512, betas=(0.0, 0.311, 0.5, 0.7), ntr=300): # ------------------------------------------------------------------ -# E1 : interference floor (MSE vs block SNR), AWGN point |h|=1 +# E1 : MSE vs block SNR at |h|=1 -- aware floor sqrt(g)/2 vs blind 1/2 # ------------------------------------------------------------------ -def E1_floor(beta=0.311, dims=(256, 768), snr_db=np.arange(0, 41, 2.5), ntr=150): - print("\n=== E1: finite-d interference floor ===") +def E1_floor(beta=0.311, dims=(256, 768), snr_db=np.arange(0, 41, 2.5), ntr=120): + print("\n=== E1: finite-d validation, aware vs blind floor ===") g = 1.0 - beta**2 - csi = C_SI(beta, 1.0 + 0j) fig, ax = plt.subplots() colors = {256: "C0", 768: "C3"} rows = [] @@ -174,444 +217,134 @@ def E1_floor(beta=0.311, dims=(256, 768), snr_db=np.arange(0, 41, 2.5), ntr=150) mc = np.zeros(len(snr_db)) for _ in range(ntr): e1, e2 = embed_pair(d, beta) - M1, M2 = two_user_masks(d, beta) + M1, M2 = haar(d), haar(d) + Q = M1.T @ M2 r0 = (M1 @ e1) + (M2 @ e2) - n = (rng.standard_normal(d) + 1j * rng.standard_normal(d)) / math.sqrt(2) + n = cnoise(d) for k, s in enumerate(snr_db): sig = 10 ** (-s / 20.0) - g1, _ = demux(r0 + sig * n, M1, M2, 1.0, 1.0, beta) + g1 = aware(M1.T @ (r0 + sig * n), Q, beta, 1.0, sig**2, d) mc[k] += np.linalg.norm(g1 - e1)**2 mc /= ntr rho = 10 ** (snr_db / 10.0) - th = d / (rho * g) + csi - ideal = d / (rho * g) + th = np.array([mse_theory(beta, 1.0, g + d / r) for r in rho]) + bl = np.array([mse_blind(1.0, d / r) for r in rho]) ax.semilogy(snr_db, mc, "o", ms=3.5, color=colors[d], mfc="none", - label=rf"MC, $d={d}$") + label=rf"Monte Carlo, $d={d}$") ax.semilogy(snr_db, th, "-", color=colors[d], label=rf"Theorem 1, $d={d}$") if d == dims[-1]: - ax.semilogy(snr_db, ideal, ":", color="k", lw=1.1, - label="Idealized (no floor)") - for s, m, t, i in zip(snr_db, mc, th, ideal): - rows.append([d, s, m, t, i]) - onset = 10 * math.log10(d / (g * csi)) - print(f" d={d}: floor C_SI={csi:.4f}, onset ~{onset:.1f} dB, " - f"max MC/theory dev " - f"{100*max(abs(mc/th-1)):.1f}%") - ax.axhline(csi, color="gray", lw=0.8, ls="--") - ax.text(1.0, csi * 1.15, r"floor $C_{\mathrm{SI}}$", fontsize=7, color="gray") + ax.semilogy(snr_db, bl, "--", color="C1", lw=1.1, + label=LBL["blind"]) + for s, m, t, b in zip(snr_db, mc, th, bl): + rows.append([d, s, m, t, b]) + dev = 100 * max(abs(mc / th - 1)) + print(f" d={d}: max MC/theory dev {dev:.1f}%") + ax.axhline(math.sqrt(g) / 2, color="gray", lw=0.8, ls="--") + ax.axhline(0.5, color="gray", lw=0.8, ls=":") + ax.text(1.0, 0.52, r"blind floor $1/2$", fontsize=7, color="gray") + ax.text(22.0, 0.40, r"aware floor $\sqrt{1-\beta^2}/2$", + fontsize=7, color="gray") + ax.set_yscale("linear") 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.5, 2000) + ax.set_xlim(0, 40); ax.set_ylim(0.4, 1.05) ax.legend(loc="upper right", ncol=1) save_fig(fig, "fig_floor") - write_csv("floor_validation", ["d", "snr_db", "mse_mc", "mse_theory", "mse_ideal"], rows) + write_csv("floor_validation", + ["d", "snr_db", "mse_mc", "mse_theory", "mse_blind"], rows) + print(f" aware floor {math.sqrt(g)/2:.4f} vs blind floor 0.5000 " + f"(ratio {0.5/(math.sqrt(g)/2):.4f} = 1/sqrt(1-beta^2))") # ------------------------------------------------------------------ -# E2 : realisable SIC vs genie SIC vs EDMA vs OMA (Rayleigh) +# E7 : effective-rate figures (eta = 1/MSE - 1) # ------------------------------------------------------------------ -def E2_sic(beta=0.311, d=512, snr_db=np.arange(0, 31, 5), ntr=400): - print("\n=== E2: realisable vs genie SIC (Rayleigh) ===") - res = {k: np.zeros(len(snr_db)) for k in - ("edma", "oma", "genie", "sic")} - for _ in range(ntr): - e1, e2 = embed_pair(d, beta) - M1, M2 = two_user_masks(d, beta) - h1, h2 = rayleigh(2) - r0 = h1 * (M1 @ e1) + h2 * (M2 @ e2) - n = (rng.standard_normal(d) + 1j * rng.standard_normal(d)) / math.sqrt(2) - n2 = (rng.standard_normal(d) + 1j * rng.standard_normal(d)) / math.sqrt(2) - for k, s in enumerate(snr_db): - sig = 10 ** (-s / 20.0) - r = r0 + sig * n - # EDMA - g1, g2 = demux(r, M1, M2, h1, h2, beta) - res["edma"][k] += 0.5 * (cosine(g1, e1) + cosine(g2, e2)) - # OMA equivalent-bandwidth model: interference-free, noise x sqrt(2) - o1 = e1 + math.sqrt(2) * sig * n / h1 - o2 = e2 + math.sqrt(2) * sig * n2 / h2 - res["oma"][k] += 0.5 * (cosine(o1, e1) + cosine(o2, e2)) - # genie SIC: perfect removal of the other user for BOTH users - ge1 = M1.T @ (r - h2 * (M2 @ e2)) / h1 - ge2 = M2.T @ (r - h1 * (M1 @ e1)) / h2 - res["genie"][k] += 0.5 * (cosine(ge1, e1) + cosine(ge2, e2)) - # realisable SIC: stronger user first (matched filter), - # unit-norm projection as the analog decision, then subtract - if abs(h1) >= abs(h2): - hs, hw, Ms, Mw, es, ew = h1, h2, M1, M2, e1, e2 - else: - hs, hw, Ms, Mw, es, ew = h2, h1, M2, M1, e2, e1 - d_s = Ms.T @ r / hs - dec_s = d_s / np.linalg.norm(d_s) # analog decision - r_res = r - hs * (Ms @ dec_s) - d_w = Mw.T @ r_res / hw - res["sic"][k] += 0.5 * (cosine(d_s, es) + cosine(d_w, ew)) - for k in res: - res[k] /= ntr - fig, ax = plt.subplots() - ax.plot(snr_db, res["edma"], "o-", color="C3", label="EDMA") - ax.plot(snr_db, res["genie"], "s--", color="C0", label="Genie-aided SIC") - ax.plot(snr_db, res["sic"], "^-.", color="C2", label="Realisable SIC") - ax.plot(snr_db, res["oma"], "v:", color="C1", label="OMA") - ax.set_xlabel("Per-block SNR $\\rho$ [dB]") - ax.set_ylabel("Mean cosine similarity") - ax.set_xlim(snr_db[0], snr_db[-1]); ax.set_ylim(0, 1) - ax.legend(loc="upper left") - save_fig(fig, "fig_sic") - rows = [[s] + [res[k][i] for k in ("edma", "oma", "genie", "sic")] - for i, s in enumerate(snr_db)] - write_csv("sic_comparison", ["snr_db", "edma", "oma", "genie", "sic"], rows) - i20 = list(snr_db).index(20) - print(f" at 20 dB: EDMA {res['edma'][i20]:.3f}, realisable SIC " - f"{res['sic'][i20]:.3f}, genie {res['genie'][i20]:.3f}, " - f"OMA {res['oma'][i20]:.3f}") +def T_edma(rho, d, beta): + m = mse_theory(beta, 1.0, (1.0 - beta**2) + d / rho) + return 2.0 * math.log2(1.0 + eta_of(m)) -# ------------------------------------------------------------------ -# E3 : Rayleigh unconditional MSE ??ZF inversion vs regularised -# ------------------------------------------------------------------ -def E3_regularised(beta=0.311, d=512, snrs=(10, 20), ntr=4000): - print("\n=== E3: Rayleigh unconditional MSE, ZF vs regularised ===") - rows = [] - for s in snrs: - sig = 10 ** (-s / 20.0) - sig2 = sig**2 - mse_zf, mse_rg = [], [] - for _ in range(ntr): - e1, e2 = embed_pair(d, beta) - M1, M2 = two_user_masks(d, beta) - h1, h2 = rayleigh(2) - r = h1 * (M1 @ e1) + h2 * (M2 @ e2) \ - + sig * (rng.standard_normal(d) + 1j * rng.standard_normal(d)) / math.sqrt(2) - g1, _ = demux(r, M1, M2, h1, h2, beta) - mse_zf.append(np.linalg.norm(g1 - e1)**2) - # regularised inversion: 1/h -> h*/(|h|^2 + d sigma^2) - eps = d * sig2 - f1 = (abs(h1)**2 + eps) / np.conj(h1) - f2 = (abs(h2)**2 + eps) / np.conj(h2) - g1r, _ = demux(r, M1, M2, f1, f2, beta) - mse_rg.append(np.linalg.norm(g1r - e1)**2) - zf_mean, zf_med = float(np.mean(mse_zf)), float(np.median(mse_zf)) - rg_mean, rg_med = float(np.mean(mse_rg)), float(np.median(mse_rg)) - print(f" {s} dB: ZF mean {zf_mean:9.2f} (median {zf_med:6.2f}) | " - f"regularised mean {rg_mean:6.3f} (median {rg_med:6.3f})") - rows.append([s, zf_mean, zf_med, rg_mean, rg_med]) - write_csv("rayleigh_mse", ["snr_db", "zf_mean", "zf_median", - "reg_mean", "reg_median"], rows) - - -# ------------------------------------------------------------------ -# E4 : imperfect CSI -# ------------------------------------------------------------------ -def E4_csi(beta=0.311, d=512, snr=30.0, - sh2=np.array([0.0, 0.01, 0.02, 0.05, 0.1, 0.2, 0.3]), ntr=400): - """EDMA cosine is CSI-direction-invariant (h-estimates cancel in the - demux direction); realisable SIC degrades through its subtraction stage.""" - print("\n=== E4: imperfect CSI robustness (EDMA vs realisable SIC) ===") - sig = 10 ** (-snr / 20.0) - res_e = np.zeros(len(sh2)); res_s = np.zeros(len(sh2)) - for _ in range(ntr): - e1, e2 = embed_pair(d, beta) - M1, M2 = two_user_masks(d, beta) - h1, h2 = rayleigh(2) - r = h1 * (M1 @ e1) + h2 * (M2 @ e2) + sig * ( - rng.standard_normal(d) + 1j * rng.standard_normal(d)) / math.sqrt(2) - eps1, eps2 = rayleigh(2) - for j, v in enumerate(sh2): - hh1 = h1 + math.sqrt(v) * eps1 - hh2 = h2 + math.sqrt(v) * eps2 - g1, g2 = demux(r, M1, M2, hh1, hh2, beta) - res_e[j] += 0.5 * (cosine(g1, e1) + cosine(g2, e2)) - # realisable SIC with the same imperfect estimates - if abs(hh1) >= abs(hh2): - hs, hw, Ms, Mw, es, ew = hh1, hh2, M1, M2, e1, e2 - else: - hs, hw, Ms, Mw, es, ew = hh2, hh1, M2, M1, e2, e1 - d_s = Ms.T @ r / hs - dec_s = d_s / np.linalg.norm(d_s) - r_res = r - hs * (Ms @ dec_s) - d_w = Mw.T @ r_res / hw - res_s[j] += 0.5 * (cosine(d_s, es) + cosine(d_w, ew)) - res_e /= ntr; res_s /= ntr - print(f" EDMA: {res_e[0]:.4f} -> {res_e[-1]:.4f} " - f"(delta {100*(res_e[0]-res_e[-1]):.2f} points)") - print(f" SIC : {res_s[0]:.4f} -> {res_s[-1]:.4f} " - f"(delta {100*(res_s[0]-res_s[-1]):.2f} points)") - fig, ax = plt.subplots() - ax.plot(sh2, res_e, "o-", color="C3", label="EDMA") - ax.plot(sh2, res_s, "^-.", color="C2", label="Realisable SIC") - ax.set_xlabel(r"CSI error variance $\sigma_h^2$") - ax.set_ylabel("Mean cosine similarity") - ax.set_xlim(0, sh2[-1]); ax.set_ylim(0, 0.7) - ax.legend(loc="lower left") - save_fig(fig, "fig_csi") - rows = [[v, res_e[j], res_s[j]] for j, v in enumerate(sh2)] - write_csv("csi_error", ["sigma_h2", "edma", "sic"], rows) - - -# ------------------------------------------------------------------ -# E5 : Walsh-Hadamard structured masks vs Haar -# ------------------------------------------------------------------ -def hadamard(n): - H = np.array([[1.0]]) - while H.shape[0] < n: - H = np.block([[H, H], [H, -H]]) - return H / math.sqrt(n) - - -def E5_maskfam(beta=0.311, d=512, snr_db=np.arange(0, 41, 5), ntr=200): - print("\n=== E5: Walsh-Hadamard masks vs Haar mixture ===") - H = hadamard(d) - g = math.sqrt(1.0 - beta**2) - res = {"haar": np.zeros(len(snr_db)), "wh": np.zeros(len(snr_db))} - for _ in range(ntr): - e1, e2 = embed_pair(d, beta) - M1, M2 = two_user_masks(d, beta) - D1 = np.diag(rng.choice([-1.0, 1.0], d)) - D2 = np.diag(rng.choice([-1.0, 1.0], d)) - W1 = H @ D1 - W2 = beta * W1 + g * (H @ D2) - r0h = (M1 @ e1) + (M2 @ e2) - r0w = (W1 @ e1) + (W2 @ e2) - n = (rng.standard_normal(d) + 1j * rng.standard_normal(d)) / math.sqrt(2) - for k, s in enumerate(snr_db): - sig = 10 ** (-s / 20.0) - g1, _ = demux(r0h + sig * n, M1, M2, 1.0, 1.0, beta) - w1, _ = demux(r0w + sig * n, W1, W2, 1.0, 1.0, beta) - res["haar"][k] += cosine(g1, e1) - res["wh"][k] += cosine(w1, e1) - for k in res: - res[k] /= ntr - dev = 100 * np.max(np.abs(res["wh"] - res["haar"])) - print(f" max |WH - Haar| cosine deviation: {dev:.2f} points") - fig, ax = plt.subplots() - ax.plot(snr_db, res["haar"], "o-", color="C3", - label=r"Haar mixture, $\mathcal{O}(d^2)$") - ax.plot(snr_db, res["wh"], "s--", color="C0", - label=r"Walsh-Hadamard, $\mathcal{O}(d\log d)$") - ax.set_xlabel("Per-block SNR $\\rho$ [dB]") - ax.set_ylabel("Mean cosine similarity") - ax.set_xlim(snr_db[0], snr_db[-1]); ax.set_ylim(0, 0.8) - ax.legend(loc="upper left") - save_fig(fig, "fig_maskfam") - rows = [[s, res["haar"][i], res["wh"][i]] for i, s in enumerate(snr_db)] - write_csv("mask_family_rev", ["snr_db", "haar", "wh"], rows) - - -# ------------------------------------------------------------------ -# E6 : high-affinity combining mode -# ------------------------------------------------------------------ -def E6_coop(d=512, snr=20.0, betas=np.linspace(0.0, 0.98, 21), ntr=100): - print("\n=== E6: high-affinity combining-mode crossover ===") - sig = 10 ** (-snr / 20.0) - pairs = [(haar(d), haar(d)) for _ in range(ntr)] - chans = [rayleigh(2) for _ in range(ntr)] - cos_dx = np.zeros(len(betas)); cos_cb = np.zeros(len(betas)) - for j, beta in enumerate(betas): - for t in range(ntr): - U1, U2 = pairs[t] - h1, h2 = chans[t] - e1, e2 = embed_pair(d, beta) - M1, M2 = two_user_masks(d, beta, U1, U2) - r = h1 * (M1 @ e1) + h2 * (M2 @ e2) + sig * ( - rng.standard_normal(d) + 1j * rng.standard_normal(d)) / math.sqrt(2) - g1, _ = demux(r, M1, M2, h1, h2, beta) - cos_dx[j] += cosine(g1, e1) - # affinity combining: coherent weights for the e1 component - a1 = h1 + beta**2 * h2 - a2 = beta * (h1 + h2) - comb = np.conj(a1) * (M1.T @ r) + np.conj(a2) * (M2.T @ r) - cos_cb[j] += cosine(comb, e1) - cos_dx /= ntr; cos_cb /= ntr - ix = np.where(cos_cb >= cos_dx)[0] - cross = betas[ix[0]] if len(ix) else float("nan") - print(f" crossover affinity ~ {cross:.2f} at rho={snr:.0f} dB") - fig, ax = plt.subplots() - ax.plot(betas, cos_dx, "o-", color="C3", label="Separation mode (demux)") - ax.plot(betas, cos_cb, "s--", color="C0", label="Combining mode") - ax.set_xlabel(r"Pairwise affinity $\beta$") - ax.set_ylabel("Mean cosine similarity") - ax.set_xlim(0, 1); ax.set_ylim(0, 0.8) - ax.legend(loc="lower left") - save_fig(fig, "fig_coop") - rows = [[b, cos_dx[i], cos_cb[i]] for i, b in enumerate(betas)] - write_csv("coop_mode", ["beta", "cos_demux", "cos_combine"], rows) - return cross - - -# ------------------------------------------------------------------ -# E7 : corrected effective-rate figures -# ------------------------------------------------------------------ -def eta_edma(rho, d, beta, csi=None): - g = 1.0 - beta**2 - if csi is None: - csi = C_bar(beta) # symmetrised constant (alternating masks) - return 1.0 / (d / (rho * g) + csi) +def T_blind(rho, d): + return 2.0 * math.log2(1.0 + 1.0 / (1.0 + d / rho)) def E7_rates(beta=0.311, d=512): - print("\n=== E7a: corrected effective-rate comparison ===") - snr_db = np.arange(0, 31, 1.0) + print("\n=== E7a: effective-rate comparison ===") + snr_db = np.arange(0, 41, 0.5) rho = 10 ** (snr_db / 10.0) - g = 1.0 - beta**2 - T_edma = 2 * np.log2(1 + eta_edma(rho, d, beta)) - T_ideal = 2 * np.log2(1 + rho * g / d) - T_oma = 2 * np.log2(1 + rho / (2 * d)) - T_genie = 2 * np.log2(1 + rho / d) - C_mac = np.log2(1 + 2 * rho / d) + Te = np.array([T_edma(r, d, beta) for r in rho]) + Tb = np.array([T_blind(r, d) for r in rho]) + To = 2 * np.log2(1 + rho / (2 * d)) + Tg = 2 * np.log2(1 + rho / d) + Cm = np.log2(1 + 2 * rho / d) fig, ax = plt.subplots() - ax.plot(snr_db, T_edma, "-", color="C3", label="EDMA (Theorem 1)") - ax.plot(snr_db, T_ideal, ":", color="C3", lw=1.1, - label="EDMA idealized (infeasible)") - ax.plot(snr_db, T_oma, "--", color="C1", label="OMA") - ax.plot(snr_db, T_genie, "-.", color="C0", label="Genie-aided SIC bound") - ax.plot(snr_db, C_mac, "-", color="k", lw=1.0, label="MAC sum capacity") + ax.plot(snr_db, Te, "-", color="C3", label=LBL["edma"]) + ax.plot(snr_db, Tb, ":", color="C4", lw=1.2, label=LBL["blind"]) + ax.plot(snr_db, To, "--", color="C1", label=LBL["oma"]) + ax.plot(snr_db, Tg, "-.", color="C0", label=LBL["genie"]) + ax.plot(snr_db, Cm, "-", color="k", lw=1.0, label=LBL["mac"]) ax.set_xlabel("Per-block SNR $\\rho$ [dB]") ax.set_ylabel("Effective sum rate [bps/Hz]") - ax.set_xlim(0, 30); ax.set_ylim(0, 3.2) + ax.set_xlim(0, 40); ax.set_ylim(0, 3.2) ax.legend(loc="upper left") save_fig(fig, "fig_rate_corrected") - rows = [[s, T_edma[i], T_ideal[i], T_oma[i], T_genie[i], C_mac[i]] + rows = [[s, Te[i], Tb[i], To[i], Tg[i], Cm[i]] for i, s in enumerate(snr_db)] write_csv("rate_corrected", - ["snr_db", "edma", "edma_ideal", "oma", "genie", "mac"], rows) + ["snr_db", "edma", "blind", "oma", "genie", "mac"], rows) i20 = list(snr_db).index(20.0) - csi = C_bar(beta) - rho_c = d * (2 - 1 / g) / csi - print(f" at 20 dB: EDMA {T_edma[i20]:.3f}, OMA {T_oma[i20]:.3f} " - f"(gain {T_edma[i20]/T_oma[i20]:.2f}x), MAC {C_mac[i20]:.3f}, " - f"EDMA/MAC {T_edma[i20]/C_mac[i20]:.3f} (gamma={g:.3f})") - print(f" OMA re-crossover rho_c = {10*math.log10(rho_c):.1f} dB") + print(f" at 20 dB: EDMA {Te[i20]:.3f}, blind {Tb[i20]:.3f}, " + f"OMA {To[i20]:.3f} (gain {Te[i20]/To[i20]:.2f}x), " + f"MAC {Cm[i20]:.3f}, EDMA/MAC {Te[i20]/Cm[i20]:.3f}") + g = 1.0 - beta**2 + rho_c = 2 * d * (2 / math.sqrt(g) - 1) + ix = np.where(To >= Te)[0] + rc_num = snr_db[ix[0]] if len(ix) else float("nan") + print(f" OMA re-crossover: floor formula {10*math.log10(rho_c):.1f} dB, " + f"numerical {rc_num:.1f} dB " + f"(blind: {10*math.log10(2*d):.1f} dB)") - print("\n=== E7b: corrected beta sweep ===") + print("\n=== E7b: value-of-affinity sweep ===") betas = np.linspace(0.0, 0.98, 99) fig, ax = plt.subplots() rows = [] for s, col in ((10, "C0"), (20, "C3")): rho_s = 10 ** (s / 10.0) - Te = np.array([2 * np.log2(1 + eta_edma(rho_s, d, b)) for b in betas]) - To = 2 * np.log2(1 + rho_s / (2 * d)) - Tg = 2 * np.log2(1 + rho_s / d) + Te = np.array([T_edma(rho_s, d, b) for b in betas]) + Tb = T_blind(rho_s, d) + To = 2 * math.log2(1 + rho_s / (2 * d)) + Tg = 2 * math.log2(1 + rho_s / d) ax.plot(betas, Te, "-", color=col, label=rf"EDMA, $\rho={s}$ dB") - ax.axhline(To, color=col, ls="--", lw=1.0, - label=rf"OMA, $\rho={s}$ dB") - ax.axhline(Tg, color=col, ls="-.", lw=0.8, - label=rf"Genie-aided SIC, $\rho={s}$ dB") - ix = np.where(Te <= To)[0] - bstar = betas[ix[0]] if len(ix) else float("nan") - print(f" rho={s} dB: crossover beta* = {bstar:.3f} " - f"(wideband limit 1/sqrt(2)=0.707)") + ax.axhline(Tb, color=col, ls=":", lw=1.0) + ax.axhline(To, color=col, ls="--", lw=1.0) + ax.axhline(Tg, color=col, ls="-.", lw=0.8) + ixg = np.where(Te >= Tg)[0] + bg = betas[ixg[0]] if len(ixg) else float("nan") + print(f" rho={s} dB: EDMA(0)/blind = {Te[0]/Tb:.3f}, " + f"EDMA(0.311) gain over blind " + f"{Te[np.argmin(abs(betas-0.311))]/Tb:.3f}x, " + f"crosses genie at beta ~ {bg:.2f}") for i, b in enumerate(betas): - rows.append([s, b, Te[i], To, Tg]) - for b0 in (0.031, 0.311): + rows.append([s, b, Te[i], Tb, To, Tg]) + for b0 in (0.030, 0.311): ax.axvline(b0, 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.02) - ax.set_yticks([0.0, 0.2, 0.4, 0.6, 0.8, 1.0]) - ax.legend(loc="upper right", ncol=1, fontsize=5.8, - handlelength=1.5, borderaxespad=0.2) + ax.set_xlim(0, 1) + ax.legend(loc="upper left") save_fig(fig, "fig_beta_sweep_corrected") write_csv("beta_sweep_corrected", - ["snr_db", "beta", "edma", "oma", "genie"], rows) - - -def E7_multiuser(beta=0.311, d=512, Us=(2, 3, 4), ntr_cal=80, ntr_mc=120): - print("\n=== E7c: corrected multi-user scaling ===") - snr_db = np.arange(0, 31, 2.5) - snr_mk = np.arange(0, 31, 5) - rho = 10 ** (snr_db / 10.0) - fig, ax = plt.subplots() - colors = {2: "C0", 3: "C2", 4: "C3"} - rows = [] - csi2 = C_SI(beta, 1.0 + 0j) - for U in Us: - B = (1 - beta) * np.eye(U) + beta * np.ones((U, U)) - Binv_uu = np.linalg.inv(B)[0, 0] - gU = 1.0 / Binv_uu - # calibrate C_SI^(U) by noise-free MC at h_u = 1 (the same - # evaluation convention as the two-user rate curves, so the - # U = 2 curve reduces exactly to T_EDMA with C_bar), - # averaged over all users (mask roles are asymmetric) - acc = 0.0 - for _ in range(ntr_cal): - A = np.linalg.cholesky(B) - Uks = [haar(d) for _ in range(U)] - Ms = [sum(A[u, k] * Uks[k] for k in range(U)) for u in range(U)] - h = np.ones(U, dtype=complex) - # symmetric equal-affinity embeddings: e_u = beta-mixed set - base = unit(rng.standard_normal(d)) - es = [] - for u in range(U): - w = rng.standard_normal(d) - w = unit(w - (w @ base) * base) - # construct so that ~ beta pairwise - es.append(unit(math.sqrt(beta) * base - + math.sqrt(1 - beta) * w)) - r = sum(h[u] * (Ms[u] @ es[u]) for u in range(U)) - Binv = np.linalg.inv(B) - # block demux e_hat_u = (1/h_u) sum_v Binv[u,v] M_v^T r - for u in range(U): - eh = sum(Binv[u, v] * (Ms[v].T @ r) for v in range(U)) / h[u] - acc += np.linalg.norm(eh - es[u])**2 - csiU = acc / (ntr_cal * U) - print(f" U={U}: C_SI^(U) = {csiU:.3f} " - f"((U-1)*C_bar = {(U-1)*C_bar(beta):.3f}), gamma_U = {gU:.3f}") - eta = 1.0 / (d * Binv_uu / rho + csiU) - T_th = U * np.log2(1 + eta) - T_oma = U * np.log2(1 + rho / (U * d)) - ax.plot(snr_db, T_th, "-", color=colors[U], label=rf"EDMA, $U={U}$") - ax.plot(snr_db, T_oma, "--", color=colors[U], lw=1.0, - label=rf"OMA, $U={U}$") - # MC markers (with noise, h_u = 1, per-realization real masks) - err_mc = np.zeros(len(snr_mk)) - for _ in range(ntr_mc): - A = np.linalg.cholesky(B) - Uks = [haar(d) for _ in range(U)] - Ms = [sum(A[u, k] * Uks[k] for k in range(U)) for u in range(U)] - h = np.ones(U, dtype=complex) - base = unit(rng.standard_normal(d)) - es = [] - for u in range(U): - w = rng.standard_normal(d) - w = unit(w - (w @ base) * base) - es.append(unit(math.sqrt(beta) * base - + math.sqrt(1 - beta) * w)) - r0 = sum(h[u] * (Ms[u] @ es[u]) for u in range(U)) - n = (rng.standard_normal(d) + 1j * rng.standard_normal(d)) / math.sqrt(2) - Binv = np.linalg.inv(B) - for k, s in enumerate(snr_mk): - sig = 10 ** (-s / 20.0) - r = r0 + sig * n - for u in range(U): - eh = sum(Binv[u, v] * (Ms[v].T @ r) for v in range(U)) / h[u] - err_mc[k] += np.linalg.norm(eh - es[u])**2 - err_mc /= ntr_mc * U - T_mc = U * np.log2(1 + 1.0 / err_mc) - ax.plot(snr_mk, T_mc, "o", color=colors[U], ms=4, mfc="none") - for i, s in enumerate(snr_db): - rows.append([U, s, T_th[i], T_oma[i]]) - i20 = list(snr_db).index(20.0) - print(f" at 20 dB: EDMA {T_th[i20]:.3f} vs OMA {T_oma[i20]:.3f} " - f"(gain {T_th[i20]/T_oma[i20]:.2f}x)") - ax.set_xlabel("Per-block SNR $\\rho$ [dB]") - ax.set_ylabel("Effective sum rate [bps/Hz]") - ax.set_xlim(0, 30); ax.set_ylim(0, 1.5) - ax.legend(loc="upper left", ncol=1, fontsize=6.2) - save_fig(fig, "fig_multiuser_corrected") - write_csv("multiuser_corrected", ["U", "snr_db", "edma", "oma"], rows) + ["snr_db", "beta", "edma", "blind", "oma", "genie"], rows) if __name__ == "__main__": import sys todo = set(sys.argv[1:]) ALL = { - "E0": E0_theorem_check, "E1": E1_floor, "E2": E2_sic, - "E3": E3_regularised, "E4": E4_csi, "E5": E5_maskfam, - "E6": E6_coop, "E7a": E7_rates, "E7c": E7_multiuser, + "E0": E0_theorem_check, "E1": E1_floor, "E7a": E7_rates, } for name, fn in ALL.items(): if not todo or name in todo: fn() - print("\nAll requested revision simulations complete.") + print("\nAll requested simulations complete.") diff --git a/code/revision_sims_gpu.py b/code/revision_sims_gpu.py new file mode 100644 index 0000000..53bf255 --- /dev/null +++ b/code/revision_sims_gpu.py @@ -0,0 +1,416 @@ +""" +GPU-accelerated Monte Carlo experiments (torch backend). +======================================================== +Computes the CSV artifacts of experiments E2, E3, E4, E5, E7c, E8 +of revision_sims.py with identical models and conventions, using +torch (CUDA when available) for the dense linear algebra. All +random draws come from the numpy generator with the documented seed +2026, so the sample stream is platform-independent; torch only +accelerates QR, matrix products, and linear solves in float32 / +complex64 precision. Figures are rendered separately by +replot_all.py, which reads only data/. + +Run under WSL: python3 revision_sims_gpu.py E2 E3 E4 E5 E7c E8 +""" +from __future__ import annotations +import csv +import math +import sys +import time +from pathlib import Path +import numpy as np +import torch + +ROOT = Path(__file__).resolve().parents[1] +CSV_DIR = ROOT / "data" + +SEED = 2026 +rng = np.random.default_rng(SEED) +DEV = "cuda" if torch.cuda.is_available() else "cpu" +print(f"[gpu] device = {DEV}") + + +def write_csv(name, header, rows): + p = CSV_DIR / f"{name}.csv" + with open(p, "w", newline="") as f: + w = csv.writer(f); w.writerow(header); w.writerows(rows) + print(f"[OK] wrote {p}") + + +def haar_g(d): + """Haar orthogonal on the GPU from a numpy Gaussian draw.""" + G = torch.tensor(rng.standard_normal((d, d)), dtype=torch.float32, + device=DEV) + Q, R = torch.linalg.qr(G) + return Q * torch.sign(torch.diagonal(R)).unsqueeze(0) + + +def embed_pair(d, beta): + e1 = rng.standard_normal(d) + e1 /= np.linalg.norm(e1) + w = rng.standard_normal(d) + w -= (w @ e1) * e1 + w /= np.linalg.norm(w) + e2 = beta * e1 + math.sqrt(1.0 - beta**2) * w + return (torch.tensor(e1, dtype=torch.float32, device=DEV), + torch.tensor(e2, dtype=torch.float32, device=DEV)) + + +def rayleigh2(): + h = (rng.standard_normal(2) + 1j * rng.standard_normal(2)) / math.sqrt(2) + return complex(h[0]), complex(h[1]) + + +def cnoise_g(d): + n = (rng.standard_normal(d) + 1j * rng.standard_normal(d)) / math.sqrt(2) + return torch.tensor(n, dtype=torch.complex64, device=DEV) + + +def aware_g(t, Q, beta, c, nvar): + """Batched aware Wiener demux. t: (b,d) cfloat, Q: (d,d) float, + c: python complex, nvar: (b,) tensor.""" + d = Q.shape[0] + b = t.shape[0] + g = 1.0 - beta * beta + rho = g * abs(c)**2 / d + nvar + A = torch.eye(d, device=DEV, dtype=torch.complex64) \ + + beta * c * Q.to(torch.complex64) + S = (A @ A.mH / d).unsqueeze(0) \ + + rho.view(b, 1, 1) * torch.eye(d, device=DEV, + dtype=torch.complex64) + x = torch.linalg.solve(S, t.unsqueeze(-1)) + return (A.mH.unsqueeze(0) @ x).squeeze(-1) / d + + +def abscos(a, e): + num = (a * e.to(a.dtype).conj()).sum(-1).abs() + return (num / (a.norm(dim=-1) * e.norm())).cpu().numpy() + + +# ------------------------------------------------------------------ +def E2_sic(beta=0.311, d=512, ntr=400): + print("\n=== E2: receiver comparison under block-Rayleigh fading ===") + snr_db = np.arange(0, 31, 2.5) + sigs = torch.tensor(10 ** (-snr_db / 20.0), dtype=torch.float32, + device=DEV) + nb = len(snr_db) + res = {k: np.zeros(nb) for k in ("edma", "blind", "oma", "genie", "sic")} + t0 = time.time() + for tr in range(ntr): + e1, e2 = embed_pair(d, beta) + M1, M2 = haar_g(d), haar_g(d) + Q = M1.T @ M2 + h1, h2 = rayleigh2() + n = cnoise_g(d); n2 = cnoise_g(d) + r0 = h1 * (M1 @ e1).to(torch.complex64) \ + + h2 * (M2 @ e2).to(torch.complex64) + r = r0.unsqueeze(0) + sigs.view(-1, 1) * n.unsqueeze(0) + t1 = (M1.T.to(torch.complex64) @ r.unsqueeze(-1)).squeeze(-1) / h1 + t2 = (M2.T.to(torch.complex64) @ r.unsqueeze(-1)).squeeze(-1) / h2 + c1, c2 = h2 / h1, h1 / h2 + v1 = sigs**2 / abs(h1)**2 + v2 = sigs**2 / abs(h2)**2 + g1 = aware_g(t1, Q, beta, c1, v1) + g2 = aware_g(t2, Q.T, beta, c2, v2) + res["edma"] += 0.5 * (abscos(g1, e1) + abscos(g2, e2)) + res["blind"] += 0.5 * (abscos(t1, e1) + abscos(t2, e2)) + o1 = e1.to(torch.complex64).unsqueeze(0) \ + + math.sqrt(2) * sigs.view(-1, 1) * n.unsqueeze(0) / h1 + o2 = e2.to(torch.complex64).unsqueeze(0) \ + + math.sqrt(2) * sigs.view(-1, 1) * n2.unsqueeze(0) / h2 + res["oma"] += 0.5 * (abscos(o1, e1) + abscos(o2, e2)) + ge1 = (M1.T.to(torch.complex64) + @ (r - h2 * (M2 @ e2).to(torch.complex64)).unsqueeze(-1) + ).squeeze(-1) / h1 + ge2 = (M2.T.to(torch.complex64) + @ (r - h1 * (M1 @ e1).to(torch.complex64)).unsqueeze(-1) + ).squeeze(-1) / h2 + res["genie"] += 0.5 * (abscos(ge1, e1) + abscos(ge2, e2)) + # realizable decision-directed SIC: the stronger user is detected + # with the same aware Wiener stage (a scalar-scaled matched-filter + # decision would re-modulate to a multiple of r itself, because + # M_s M_s^T = I, and cancel nothing), its re-modulated estimate is + # subtracted, and the weaker user is read from the residual. + if abs(h1) >= abs(h2): + hs, hw, Ms, Mw, es, ew = h1, h2, M1, M2, e1, e2 + Qsw, csw, vsw = Q, c1, v1 + else: + hs, hw, Ms, Mw, es, ew = h2, h1, M2, M1, e2, e1 + Qsw, csw, vsw = Q.T, c2, v2 + t_s = (Ms.T.to(torch.complex64) @ r.unsqueeze(-1)).squeeze(-1) / hs + dec = aware_g(t_s, Qsw, beta, csw, vsw) + r_res = r - hs * (Ms.to(torch.complex64) + @ dec.unsqueeze(-1)).squeeze(-1) + d_w = (Mw.T.to(torch.complex64) @ r_res.unsqueeze(-1)).squeeze(-1) / hw + res["sic"] += 0.5 * (abscos(dec, es) + abscos(d_w, ew)) + if (tr + 1) % 100 == 0: + print(f" {tr+1}/{ntr} ({time.time()-t0:.0f}s)", flush=True) + for k in res: + res[k] /= ntr + rows = [[s] + [res[k][i] for k in ("edma", "blind", "oma", "genie", "sic")] + for i, s in enumerate(snr_db)] + write_csv("sic_comparison", + ["snr_db", "edma", "blind", "oma", "genie", "sic"], rows) + i20 = list(snr_db).index(20) + print(f" at 20 dB: EDMA {res['edma'][i20]:.3f}, blind " + f"{res['blind'][i20]:.3f}, SIC {res['sic'][i20]:.3f}, " + f"genie {res['genie'][i20]:.3f}, OMA {res['oma'][i20]:.3f}") + + +# ------------------------------------------------------------------ +def E3_unconditional(beta=0.311, d=512, ntr=1500): + print("\n=== E3: Rayleigh unconditional MSE of the aware receiver ===") + rows = [] + for s in (10, 20): + sig = 10 ** (-s / 20.0) + mses, bl = [], [] + for _ in range(ntr): + e1, e2 = embed_pair(d, beta) + M1, M2 = haar_g(d), haar_g(d) + Q = M1.T @ M2 + h1, h2 = rayleigh2() + n = cnoise_g(d) + r = h1 * (M1 @ e1).to(torch.complex64) \ + + h2 * (M2 @ e2).to(torch.complex64) + sig * n + t1 = (M1.T.to(torch.complex64) @ r) / h1 + c1 = h2 / h1 + nv = torch.tensor([sig**2 / abs(h1)**2], device=DEV) + g1 = aware_g(t1.unsqueeze(0), Q, beta, c1, nv)[0] + mses.append(float((g1 - e1.to(torch.complex64)).norm()**2)) + lam = (1.0 / d) / (1.0 / d + abs(c1)**2 / d + sig**2 / abs(h1)**2) + b1 = lam * t1 + bl.append(float((b1 - e1.to(torch.complex64)).norm()**2)) + rows.append([s, float(np.mean(mses)), float(np.median(mses)), + float(np.mean(bl)), float(np.median(bl))]) + print(f" {s} dB: aware mean {rows[-1][1]:.4f} " + f"(median {rows[-1][2]:.4f}) | blind mean {rows[-1][3]:.4f} " + f"(median {rows[-1][4]:.4f})") + write_csv("rayleigh_mse", ["snr_db", "aware_mean", "aware_median", + "blind_mean", "blind_median"], rows) + + +# ------------------------------------------------------------------ +def E4_csi(beta=0.311, d=512, snr=30.0, ntr=400): + print("\n=== E4: imperfect CSI robustness (EDMA vs realizable SIC) ===") + sh2 = np.array([0.0, 0.01, 0.02, 0.05, 0.1, 0.2, 0.3]) + sig = 10 ** (-snr / 20.0) + res_e = np.zeros(len(sh2)); res_s = np.zeros(len(sh2)) + for _ in range(ntr): + e1, e2 = embed_pair(d, beta) + M1, M2 = haar_g(d), haar_g(d) + Q = M1.T @ M2 + h1, h2 = rayleigh2() + n = cnoise_g(d) + r = h1 * (M1 @ e1).to(torch.complex64) \ + + h2 * (M2 @ e2).to(torch.complex64) + sig * n + eps1, eps2 = rayleigh2() + for j, v in enumerate(sh2): + hh1 = h1 + math.sqrt(v) * eps1 + hh2 = h2 + math.sqrt(v) * eps2 + t1 = (M1.T.to(torch.complex64) @ r) / hh1 + t2 = (M2.T.to(torch.complex64) @ r) / hh2 + nv1 = torch.tensor([sig**2 / abs(hh1)**2], device=DEV) + nv2 = torch.tensor([sig**2 / abs(hh2)**2], device=DEV) + g1 = aware_g(t1.unsqueeze(0), Q, beta, hh2 / hh1, nv1)[0] + g2 = aware_g(t2.unsqueeze(0), Q.T, beta, hh1 / hh2, nv2)[0] + res_e[j] += 0.5 * (float(abscos(g1.unsqueeze(0), e1)[0]) + + float(abscos(g2.unsqueeze(0), e2)[0])) + if abs(hh1) >= abs(hh2): + hs, hw, Ms, Mw, es, ew = hh1, hh2, M1, M2, e1, e2 + Qsw, csw = Q, hh2 / hh1 + else: + hs, hw, Ms, Mw, es, ew = hh2, hh1, M2, M1, e2, e1 + Qsw, csw = Q.T, hh1 / hh2 + t_s = (Ms.T.to(torch.complex64) @ r) / hs + nvs = torch.tensor([sig**2 / abs(hs)**2], device=DEV) + dec = aware_g(t_s.unsqueeze(0), Qsw, beta, csw, nvs)[0] + r_res = r - hs * (Ms.to(torch.complex64) @ dec) + d_w = (Mw.T.to(torch.complex64) @ r_res) / hw + res_s[j] += 0.5 * (float(abscos(dec.unsqueeze(0), es)[0]) + + float(abscos(d_w.unsqueeze(0), ew)[0])) + res_e /= ntr; res_s /= ntr + print(f" EDMA: {res_e[0]:.4f} -> {res_e[-1]:.4f} " + f"(delta {100*(res_e[0]-res_e[-1]):.2f} points)") + print(f" SIC : {res_s[0]:.4f} -> {res_s[-1]:.4f} " + f"(delta {100*(res_s[0]-res_s[-1]):.2f} points)") + rows = [[v, res_e[j], res_s[j]] for j, v in enumerate(sh2)] + write_csv("csi_error", ["sigma_h2", "edma", "sic"], rows) + + +# ------------------------------------------------------------------ +def E5_maskfam(beta=0.311, d=512, ntr=200): + print("\n=== E5: Walsh-Hadamard diagonal variant vs Haar ===") + snr_db = np.arange(0, 41, 5) + sigs = torch.tensor(10 ** (-snr_db / 20.0), dtype=torch.float32, + device=DEV) + nb = len(snr_db) + H = np.array([[1.0]]) + while H.shape[0] < d: + H = np.block([[H, H], [H, -H]]) + Ht = torch.tensor(H / math.sqrt(d), dtype=torch.float32, device=DEV) + g = 1.0 - beta**2 + res = {"haar": np.zeros(nb), "wh": np.zeros(nb)} + exact = np.zeros(nb) + for _ in range(ntr): + e1, e2 = embed_pair(d, beta) + M1, M2 = haar_g(d), haar_g(d) + Q = M1.T @ M2 + D1 = torch.tensor(np.sign(rng.standard_normal(d)), + dtype=torch.float32, device=DEV) + D2 = torch.tensor(np.sign(rng.standard_normal(d)), + dtype=torch.float32, device=DEV) + W1, W2 = Ht * D1.unsqueeze(0), Ht * D2.unsqueeze(0) + q = D1 * D2 + n = cnoise_g(d) + r0h = (M1 @ e1 + M2 @ e2).to(torch.complex64) + r0w = (W1 @ e1 + W2 @ e2).to(torch.complex64) + rh = r0h.unsqueeze(0) + sigs.view(-1, 1) * n.unsqueeze(0) + rw = r0w.unsqueeze(0) + sigs.view(-1, 1) * n.unsqueeze(0) + t1 = (M1.T.to(torch.complex64) @ rh.unsqueeze(-1)).squeeze(-1) + g1 = aware_g(t1, Q, beta, 1.0, sigs**2) + res["haar"] += abscos(g1, e1) + tw = (W1.T.to(torch.complex64) @ rw.unsqueeze(-1)).squeeze(-1) + a = 1.0 + beta * q # (d,) + rho = g / d + sigs**2 # (nb,) + wdiag = a.unsqueeze(0) / (a.unsqueeze(0)**2 / d + + rho.view(-1, 1)) # (nb,d) + w1 = (wdiag.to(torch.complex64) / d) * tw + res["wh"] += abscos(w1, e1) + exact += ((g + d * sigs.view(-1, 1)**2) + / (a.unsqueeze(0)**2 + g + d * sigs.view(-1, 1)**2) + ).mean(1).cpu().numpy() + for k in res: + res[k] /= ntr + exact /= ntr + print(f" max |WH - Haar| cosine dev: " + f"{100*np.max(np.abs(res['wh']-res['haar'])):.2f} points; " + f"40 dB WH {res['wh'][-1]:.4f} vs Haar {res['haar'][-1]:.4f}") + rows = [[s, res["haar"][i], res["wh"][i], exact[i]] + for i, s in enumerate(snr_db)] + write_csv("mask_family_rev", ["snr_db", "haar", "wh", "wh_exact_mse"], + rows) + + +# ------------------------------------------------------------------ +def E7_multiuser(beta=0.311, d=512, ntr=100): + print("\n=== E7c: multi-user scaling (joint Wiener) ===") + snr_db = np.arange(0, 31, 2.5) + sigs = torch.tensor(10 ** (-snr_db / 20.0), dtype=torch.float32, + device=DEV) + nb = len(snr_db) + rows = [] + for U in (2, 3, 4): + B = (1 - beta) * np.eye(U) + beta * np.ones((U, U)) + A = np.linalg.cholesky(B) + Bt = torch.tensor(B, dtype=torch.float32, device=DEV) + err = np.zeros(nb) + t0 = time.time() + for _ in range(ntr): + F = rng.standard_normal((d, U)) + Fq, _ = np.linalg.qr(F) + E = (Fq @ A.T).T + Et = torch.tensor(E, dtype=torch.float32, device=DEV) + masks = [haar_g(d) for _ in range(U)] + n = cnoise_g(d) + r0 = sum(masks[u] @ Et[u] for u in range(U)).to(torch.complex64) + Qs = [masks[0].T @ masks[v] for v in range(U)] + Ret = sum(Bt[0, v] * Qs[v].T for v in range(U)) / d + S0 = sum(Bt[v, w] * (Qs[v] @ Qs[w].T) + for v in range(U) for w in range(U)) / d + r = r0.unsqueeze(0) + sigs.view(-1, 1) * n.unsqueeze(0) + t = (masks[0].T.to(torch.complex64) + @ r.unsqueeze(-1)).squeeze(-1) + S = S0.to(torch.complex64).unsqueeze(0) \ + + (sigs**2).view(-1, 1, 1) \ + * torch.eye(d, device=DEV, dtype=torch.complex64) + x = torch.linalg.solve(S, t.unsqueeze(-1)) + eh = (Ret.to(torch.complex64).unsqueeze(0) @ x).squeeze(-1) + err += ((eh - Et[0].to(torch.complex64)).norm(dim=1)**2 + ).cpu().numpy() + err /= ntr + T_mc = U * np.log2(1.0 / err) + r0v = (U - 1) + d / 10 ** (snr_db / 10.0) + T_bl = U * np.log2(1.0 + 1.0 / r0v) + T_oma = U * np.log2(1 + 10 ** (snr_db / 10.0) / (U * d)) + for i, s in enumerate(snr_db): + rows.append([U, s, T_mc[i], T_bl[i], T_oma[i], err[i]]) + i20 = list(snr_db).index(20.0) + print(f" U={U}: at 20 dB EDMA {T_mc[i20]:.3f} vs blind " + f"{T_bl[i20]:.3f} vs OMA {T_oma[i20]:.3f} " + f"(gain {T_mc[i20]/T_oma[i20]:.2f}x), floor MSE {err[-1]:.4f}" + f" [{time.time()-t0:.0f}s]") + write_csv("multiuser_corrected", + ["U", "snr_db", "edma_mc", "blind", "oma", "mse_mc"], rows) + + +# ------------------------------------------------------------------ +def E8_mismatch(beta=0.311, d=512, snr=20.0, ntr=200): + print("\n=== E8: affinity mismatch of the aware receiver ===") + sig = 10 ** (-snr / 20.0) + bhs = [b for b in (beta - 0.1, beta - 0.06, beta, beta + 0.06, + beta + 0.1, beta + 2 ** -8, 0.0) if b >= 0] + accs = np.zeros(len(bhs)); msea = np.zeros(len(bhs)) + for _ in range(ntr): + e1, e2 = embed_pair(d, beta) + M1, M2 = haar_g(d), haar_g(d) + Q = M1.T @ M2 + n = cnoise_g(d) + r = (M1 @ e1 + M2 @ e2).to(torch.complex64) + sig * n + t1 = (M1.T.to(torch.complex64) @ r) + nv = torch.tensor([sig**2], device=DEV) + for j, bh in enumerate(bhs): + g1 = aware_g(t1.unsqueeze(0), Q, bh, 1.0, nv)[0] + accs[j] += float(abscos(g1.unsqueeze(0), e1)[0]) + msea[j] += float((g1 - e1.to(torch.complex64)).norm()**2) + accs /= ntr; msea /= ntr + rows = [] + for j, bh in enumerate(bhs): + tag = ("quant b=7" if abs(bh - beta - 2**-8) < 1e-12 else + ("blind" if bh == 0.0 else f"delta={bh-beta:+.2f}")) + print(f" beta_hat={bh:.4f} ({tag}): cosine {accs[j]:.4f}, " + f"MSE {msea[j]:.4f}") + rows.append([bh, accs[j], msea[j]]) + write_csv("mismatch", ["beta_hat", "cosine", "mse"], rows) + + + + +# ------------------------------------------------------------------ +def E9_ceiling(d=512, snr=60.0, ntr=200): + print(chr(10) + '=== E9: cosine-ceiling verification (h=1) ===') + sig = 10 ** (-snr / 20.0) + rows = [] + for beta in (0.311, 0.8): + g = 1.0 - beta**2 + acc_a = 0.0; acc_b = 0.0 + for _ in range(ntr): + e1, e2 = embed_pair(d, beta) + M1, M2 = haar_g(d), haar_g(d) + Q = M1.T @ M2 + n = cnoise_g(d) + r = (M1 @ e1 + M2 @ e2).to(torch.complex64) + sig * n + t1 = (M1.T.to(torch.complex64) @ r) + nv = torch.tensor([sig**2], device=DEV) + g1 = aware_g(t1.unsqueeze(0), Q, beta, 1.0, nv)[0] + acc_a += float(abscos(g1.unsqueeze(0), e1)[0]) + acc_b += float(abscos(t1.unsqueeze(0), e1)[0]) + acc_a /= ntr; acc_b /= ntr + import math as _m + pred_a = _m.sqrt(1.0 - _m.sqrt(g) / 2.0) + pred_b = _m.sqrt(0.5) + print(f' beta={beta}: aware MC {acc_a:.4f} pred {pred_a:.4f} | ' + f'blind MC {acc_b:.4f} pred {pred_b:.4f}') + rows.append([beta, acc_a, pred_a, acc_b, pred_b]) + write_csv('cosine_ceiling', ['beta', 'aware_mc', 'aware_pred', + 'blind_mc', 'blind_pred'], rows) + + +if __name__ == "__main__": + todo = set(sys.argv[1:]) + ALL = {"E2": E2_sic, "E3": E3_unconditional, "E4": E4_csi, + "E5": E5_maskfam, "E7c": E7_multiuser, "E8": E8_mismatch, + "E9": E9_ceiling} + for name, fn in ALL.items(): + if not todo or name in todo: + fn() + print("\nAll requested GPU simulations complete.") diff --git a/code/verify_math.py b/code/verify_math.py index 0a4ccd4..b779c6d 100644 --- a/code/verify_math.py +++ b/code/verify_math.py @@ -1,70 +1,35 @@ """ -Complete numerical verification of every closed form in the manuscript. -======================================================================= -Each check implements the formula EXACTLY as printed in main.tex and -compares it against a direct Monte-Carlo or algebraic evaluation. -Prints PASS/FAIL per item with the achieved deviation. Fixed seed. +Monte Carlo verification of every closed-form claim (v2 design). +================================================================ +Independent Haar masks + affinity-aware Wiener demultiplexer. +Checks (d = 256 for speed; deviations shrink as O(1/d)): - V1 per-realization Gram identity M1^T M2 = beta I + sqrt(g) Q - V2 Theorem 1 full MSE (noise + C_SI,u) vs MC, random complex h - V3 noise-free calibration of C_SI,1 / C_SI,2 (several phases) - V4 quoted constants: C_SI,1, C_SI,2, C-bar at (0.311, h=1); - cosine ceiling 1/sqrt(1+C_SI,1) = 0.70; rho_f = 28 dB at d=768 - V5 SINR corollary eta_u = 1/MSE (per-coordinate accounting) - V6 C_SI,u >= 1 for all beta (proof identities gamma*C_SI,1 = - gamma + 4 beta^4, gamma*C_SI,2 = 1 + 3 beta^2 at h=1) - V7 Proposition (MAC consistency) on a (beta, rho) grid - V8 wideband limit T/C_MAC -> gamma - V9 beta* crossover roots at 10/20 dB (0.700 / 0.590, d=512) - V10 rho_c = d(2-1/gamma)/C-bar exact iff-condition + 25.7 dB value - V11 idealized no-floor variant crosses C_MAC at 2 beta^2 d/gamma^2 - (~21 dB at d=512, beta=0.311) - V12 mismatch identity (eq:mismatch) + bound value 8.8e-3 - V13 CSI-direction invariance: |cos| unchanged under wrong h-hat; - eq:csi-free equals eq:correct - V14 cross-moment lemma E[n^H M_u M_v^T n] = sigma^2 beta d - V15 multi-user [B^-1]_uu Sherman-Morrison formula, U = 2..6 - V16 multi-user noise-free C_SI^(U) ~ (U-1) C-bar (within 10 %) - V17 Walsh-Hadamard masks: exact orthogonality + expected cross-Gram + V1 Theorem 1 MSE formula vs MC at several (beta, SNR), h = 1 + V2 Theorem 1 under random channel phases, both users + V3 floors: aware sqrt(g)/2 vs blind 1/2, and the value ratio + V4 cosine ceiling sqrt(1 - MSE) (Corollary: cosine) + V5 blind receiver == matched filter in cosine (scalar shrinkage) + V6 monotonicity of the MSE in beta (Proposition) + V7 full-cooperation bound T <= log2(1+4 rho/d), equality at beta=1 + V8 MAC condition gamma^2 (2+k) >= 2 beta^2 k^2 boundary + V9 Walsh-Hadamard diagonal variant: exact finite-d closed form + V10 mismatch stationarity: MSE(beta_hat) - MSE(beta) = O(delta^2) + V11 correlated-mask alternative floor 1 + 4 beta^4 / gamma + (Remark and Appendix), dominated by the aware receiver + +Pure numpy, fixed seed, ~2 minutes on a laptop. """ from __future__ import annotations import math import numpy as np -def hadamard(n): - H = np.array([[1.0]]) - while H.shape[0] < n: - H = np.block([[H, H], [H, -H]]) - return H - - -def brentq(f, a, b, tol=1e-12): - fa, fb = f(a), f(b) - assert fa * fb < 0, "no sign change" - for _ in range(200): - m = 0.5 * (a + b) - fm = f(m) - if abs(fm) < tol or (b - a) < tol: - return m - if fa * fm < 0: - b, fb = m, fm - else: - a, fa = m, fm - return 0.5 * (a + b) - rng = np.random.default_rng(2026) -FAIL = [] - - -def report(name, ok, detail): - tag = "PASS" if ok else "FAIL" - if not ok: - FAIL.append(name) - print(f"[{tag}] {name}: {detail}") +D = 256 def haar(d): - Q, R = np.linalg.qr(rng.standard_normal((d, d))) + G = rng.standard_normal((d, d)) + Q, R = np.linalg.qr(G) return Q * np.sign(np.diag(R)) @@ -72,303 +37,225 @@ def unit(v): return v / np.linalg.norm(v) -def pair(d, beta): +def cosim(a, b): + return float(abs(np.vdot(a, b)) / (np.linalg.norm(a) * np.linalg.norm(b))) + + +def embed_pair(d, beta): e1 = unit(rng.standard_normal(d)) w = rng.standard_normal(d) w = unit(w - (w @ e1) * e1) - return e1, beta * e1 + math.sqrt(1 - beta**2) * w + return e1, beta * e1 + math.sqrt(1 - beta * beta) * w -def csi1(beta, c): - g = 1 - beta**2 - n2 = 1 + beta**2 * abs(c)**2 + 2 * beta**2 * np.real(c) - return (g**2 * abs(c)**2 + beta**2 * n2) / g +def cnoise(d): + return (rng.standard_normal(d) + 1j * rng.standard_normal(d)) / math.sqrt(2) -def csi2(beta, c): - g = 1 - beta**2 - return (abs(c)**2 + beta**2 + 2 * beta**2 * np.real(c)) / g +def mse_theory(beta, c1, rho_e): + a0 = 1.0 + beta**2 * abs(c1)**2 + rho_e + return rho_e / math.sqrt(a0 * a0 - 4.0 * beta**2 * abs(c1)**2) -# ---------------- V1: per-realization Gram identity ---------------- -d, beta = 256, 0.311 -g = 1 - beta**2 -U1, U2 = haar(d), haar(d) -M1, M2 = U1, beta * U1 + math.sqrt(g) * U2 -dev = np.abs(M1.T @ M2 - (beta * np.eye(d) - + math.sqrt(g) * U1.T @ U2)).max() -report("V1 Gram identity", dev < 1e-12, f"max dev {dev:.2e}") +def aware(t1, Q, beta, c1, nvar, d): + g = 1.0 - beta * beta + rho = g * abs(c1)**2 / d + nvar + S = beta * (c1 * Q + np.conj(c1) * Q.T) / d + S[np.diag_indices(d)] += (1.0 + beta**2 * abs(c1)**2) / d + rho + x = np.linalg.solve(S, t1) + return (x + beta * np.conj(c1) * (Q.T @ x)) / d -# ---------------- V2: Theorem 1 full MSE, random complex h --------- -d = 512 -for beta in (0.1, 0.311, 0.5): - g = 1 - beta**2 - h = (rng.standard_normal(2) + 1j * rng.standard_normal(2)) / math.sqrt(2) - h1, h2 = h - rho_db = 15.0 - sig = 10 ** (-rho_db / 20.0) - e1, e2 = pair(d, beta) - mc = np.zeros(2) - NT = 300 - for _ in range(NT): - U1, U2 = haar(d), haar(d) - M1, M2 = U1, beta * U1 + math.sqrt(g) * U2 - n = (rng.standard_normal(d) + 1j * rng.standard_normal(d)) \ - / math.sqrt(2) - r = h1 * (M1 @ e1) + h2 * (M2 @ e2) + sig * n - t1 = M1.T @ r / h1 - t2 = M2.T @ r / h2 - g1 = (t1 - beta * (h2 / h1) * t2) / g - g2 = (t2 - beta * (h1 / h2) * t1) / g - mc[0] += np.linalg.norm(g1 - e1)**2 - mc[1] += np.linalg.norm(g2 - e2)**2 - mc /= NT - th1 = d * sig**2 / (abs(h1)**2 * g) + csi1(beta, h2 / h1) - th2 = d * sig**2 / (abs(h2)**2 * g) + csi2(beta, h1 / h2) - dev = max(abs(mc[0] / th1 - 1), abs(mc[1] / th2 - 1)) - report(f"V2 Theorem 1 MSE (beta={beta})", dev < 0.02, - f"MC/theory dev {100*dev:.2f}% (O(1/d) at d={d})") -# ---------------- V3: noise-free C_SI calibration ------------------ -d = 512 -for phase in (0.0, math.pi / 3, math.pi): - beta = 0.311 - g = 1 - beta**2 - h1 = 1.0 + 0j - h2 = np.exp(1j * phase) - e1, e2 = pair(d, beta) - mc = np.zeros(2) - NT = 200 - for _ in range(NT): - U1, U2 = haar(d), haar(d) - M1, M2 = U1, beta * U1 + math.sqrt(g) * U2 - r = h1 * (M1 @ e1) + h2 * (M2 @ e2) - t1 = M1.T @ r / h1 - t2 = M2.T @ r / h2 - g1 = (t1 - beta * (h2 / h1) * t2) / g - g2 = (t2 - beta * (h1 / h2) * t1) / g - mc[0] += np.linalg.norm(g1 - e1)**2 - mc[1] += np.linalg.norm(g2 - e2)**2 - mc /= NT - t1v, t2v = csi1(beta, h2 / h1), csi2(beta, h1 / h2) - dev = max(abs(mc[0] / t1v - 1), abs(mc[1] / t2v - 1)) - report(f"V3 noise-free C_SI (phase={phase:.2f})", dev < 0.02, - f"dev {100*dev:.2f}%") +def run_pair(beta, sig, h1=1.0 + 0j, h2=1.0 + 0j, d=D): + e1, e2 = embed_pair(d, beta) + M1, M2 = haar(d), haar(d) + Q = M1.T @ M2 + r = h1 * (M1 @ e1) + h2 * (M2 @ e2) + sig * cnoise(d) + t1 = M1.T @ r / h1 + return e1, e2, Q, t1, M2.T @ r / h2 -# ---------------- V4: quoted constants ----------------------------- + +def check(name, ok, detail=""): + print(f"[{'PASS' if ok else 'FAIL'}] {name} {detail}") + return ok + + +allok = True + +# ---------------------------------------------------------------- V1 +devs = [] +for beta in (0.0, 0.311, 0.6): + for snr in (10.0, 20.0, 60.0): + sig = 10 ** (-snr / 20.0) + mc = 0.0 + NT = 40 + for _ in range(NT): + e1, _, Q, t1, _ = run_pair(beta, sig) + g1 = aware(t1, Q, beta, 1.0, sig * sig, D) + mc += float(np.linalg.norm(g1 - e1) ** 2) + mc /= NT + th = mse_theory(beta, 1.0, (1 - beta**2) + D * sig * sig) + devs.append(abs(mc / th - 1)) +allok &= check("V1 Theorem 1 (h=1)", max(devs) < 0.03, + f"max dev {100*max(devs):.2f}%") + +# ---------------------------------------------------------------- V2 +devs = [] +sig = 10 ** (-20.0 / 20.0) +for beta in (0.311, 0.5): + for _ in range(30): + h1 = np.exp(1j * rng.uniform(0, 2 * np.pi)) + h2 = np.exp(1j * rng.uniform(0, 2 * np.pi)) + e1, e2, Q, t1, t2 = run_pair(beta, sig, h1, h2) + c1, c2 = h2 / h1, h1 / h2 + g1 = aware(t1, Q, beta, c1, sig**2, D) + g2 = aware(t2, Q.T, beta, c2, sig**2, D) + g = 1 - beta**2 + th1 = mse_theory(beta, c1, g * abs(c1)**2 + D * sig**2) + th2 = mse_theory(beta, c2, g * abs(c2)**2 + D * sig**2) + devs.append(abs(np.linalg.norm(g1 - e1)**2 / th1 - 1)) + devs.append(abs(np.linalg.norm(g2 - e2)**2 / th2 - 1)) +allok &= check("V2 Theorem 1 (random phases, both users)", + float(np.mean(devs)) < 0.05, + f"mean dev {100*float(np.mean(devs)):.2f}%") + +# ---------------------------------------------------------------- V3 +beta = 0.6 +sig = 1e-3 +mc_a, mc_b = 0.0, 0.0 +NT = 40 +for _ in range(NT): + e1, _, Q, t1, _ = run_pair(beta, sig) + g1 = aware(t1, Q, beta, 1.0, sig * sig, D) + mc_a += float(np.linalg.norm(g1 - e1) ** 2) + lam = (1.0 / D) / (2.0 / D + sig * sig) + mc_b += float(np.linalg.norm(lam * t1 - e1) ** 2) +mc_a /= NT +mc_b /= NT +fa, fb = math.sqrt(1 - beta**2) / 2, 0.5 +allok &= check("V3 floors sqrt(g)/2 vs 1/2", + abs(mc_a - fa) < 0.02 and abs(mc_b - fb) < 0.02, + f"aware {mc_a:.4f}~{fa:.4f}, blind {mc_b:.4f}~{fb:.4f}, " + f"ratio {mc_b/mc_a:.3f}~{1/math.sqrt(1-beta**2):.3f}") + +# ---------------------------------------------------------------- V4 +acc = 0.0 +for _ in range(NT): + e1, _, Q, t1, _ = run_pair(beta, sig) + acc += cosim(aware(t1, Q, beta, 1.0, sig * sig, D), e1) +acc /= NT +pred = math.sqrt(1 - fa) +allok &= check("V4 cosine ceiling sqrt(1-MSE)", abs(acc - pred) < 0.01, + f"MC {acc:.4f} vs {pred:.4f}") + +# ---------------------------------------------------------------- V5 +e1, _, Q, t1, _ = run_pair(0.311, 0.1) +lam = 0.37 # any scalar +allok &= check("V5 blind == MF in cosine", + abs(cosim(lam * t1, e1) - cosim(t1, e1)) < 1e-12) + +# ---------------------------------------------------------------- V6 +k = D / 100.0 +vals = [mse_theory(b, 1.0, (1 - b * b) + k) + for b in np.linspace(0, 0.99, 50)] +allok &= check("V6 monotonic decrease in beta", + all(x > y for x, y in zip(vals, vals[1:]))) + +# ---------------------------------------------------------------- V7 +ok7 = True +worst = 0.0 +for rho in (1.0, 100.0, 1e4): + kk = D / rho + coop = math.log2(1 + 4 * rho / D) + for b in np.linspace(0, 1.0, 41): + m = mse_theory(b, 1.0, (1 - b * b) + kk) + T = 2 * math.log2(1 / m) + ok7 &= T <= coop + 1e-9 + worst = max(worst, T - coop) + m1 = mse_theory(1.0, 1.0, kk) + ok7 &= abs(2 * math.log2(1 / m1) - coop) < 1e-9 +allok &= check("V7 full-cooperation bound, equality at beta=1", ok7, + f"max T-coop {worst:.2e}") + +# ---------------------------------------------------------------- V8 +ok8 = True +for rho in (1.0, 10.0, 100.0, 1e3): + kk = D / rho + for b in (0.1, 0.311, 0.6, 0.9): + g = 1 - b * b + m = mse_theory(b, 1.0, g + kk) + T = 2 * math.log2(1 / m) + mac = math.log2(1 + 2 * rho / D) + lhs = g * g * (2 + kk) + rhs = 2 * b * b * kk * kk + ok8 &= (T <= mac + 1e-9) == (lhs >= rhs - 1e-9) +allok &= check("V8 MAC-condition boundary", ok8) + +# ---------------------------------------------------------------- V9 beta = 0.311 g = 1 - beta**2 -c1v, c2v = csi1(beta, 1.0 + 0j), csi2(beta, 1.0 + 0j) -cbar = (c1v + c2v) / 2 -ceil1 = 1 / math.sqrt(1 + c1v) -rho_f_db = 10 * math.log10(768 * g / c1v) -ok = (abs(cbar - 1.2349) < 5e-4 and abs(ceil1 - 0.70) < 5e-3 - and abs(rho_f_db - 28) < 0.5) -report("V4 quoted constants", ok, - f"C_SI,1 {c1v:.4f}, C_SI,2 {c2v:.4f}, C-bar {cbar:.4f} " - f"(quoted 1.2349), ceiling {ceil1:.4f} (quoted 0.70), " - f"rho_f {rho_f_db:.1f} dB (quoted 28)") +sig = 10 ** (-20.0 / 20.0) +H = np.array([[1.0]]) +while H.shape[0] < D: + H = np.block([[H, H], [H, -H]]) +H /= math.sqrt(D) +mc, th = 0.0, 0.0 +for _ in range(30): + e1, e2 = embed_pair(D, beta) + D1 = np.sign(rng.standard_normal(D)) + D2 = np.sign(rng.standard_normal(D)) + W1, W2 = H * D1[None, :], H * D2[None, :] + r = W1 @ e1 + W2 @ e2 + sig * cnoise(D) + t1 = W1.T @ r + q = D1 * D2 + a = 1.0 + beta * q + rho = g / D + sig * sig + w1 = (a / (a * a / D + rho)) * t1 / D + mc += float(np.linalg.norm(w1 - e1) ** 2) + th += float(np.mean((g + D * sig**2) / (a * a + g + D * sig**2))) +allok &= check("V9 WH exact finite-d closed form", + abs(mc / th - 1) < 0.03, f"dev {100*abs(mc/th-1):.2f}%") -# ---------------- V5: SINR = 1/MSE --------------------------------- -rho = 10 ** (15 / 10) -eta = 1 / (512 / (rho * g) + c1v) -mse = 512 / (rho * g) + c1v -report("V5 SINR corollary", abs(eta * mse - 1) < 1e-12, - f"eta*MSE = {eta*mse:.6f}") - -# ---------------- V6: C_SI >= 1 and proof identities --------------- -ok = True -worst = 1e9 -for b in np.linspace(0.0, 0.99, 200): - gg = 1 - b**2 - lhs1 = gg * csi1(b, 1.0 + 0j) - lhs2 = gg * csi2(b, 1.0 + 0j) - if abs(lhs1 - (gg + 4 * b**4)) > 1e-12: ok = False - if abs(lhs2 - (1 + 3 * b**2)) > 1e-12: ok = False - worst = min(worst, csi1(b, 1.0 + 0j), csi2(b, 1.0 + 0j)) -report("V6 C_SI >= 1 + proof identities", ok and worst >= 1 - 1e-12, - f"min C_SI over beta grid = {worst:.6f}") - -# ---------------- V7: MAC consistency on a grid -------------------- -def T_edma(b, r_, d_): - gg = 1 - b**2 - cb = (csi1(b, 1 + 0j) + csi2(b, 1 + 0j)) / 2 - return 2 * np.log2(1 + 1 / (d_ / (r_ * gg) + cb)) - -ok = True -for b in np.linspace(0, 0.95, 40): - for rdb in np.linspace(-10, 60, 60): - r_ = 10 ** (rdb / 10) - gg = 1 - b**2 - mid = np.log2(1 + 2 * r_ * gg / 512) - cmac = np.log2(1 + 2 * r_ / 512) - if T_edma(b, r_, 512) > mid + 1e-12 or mid > cmac + 1e-12: - ok = False -report("V7 MAC consistency grid", ok, "T_EDMA <= log2(1+2 rho g/d) <= C_MAC") - -# ---------------- V8: wideband limit ------------------------------- -b = 0.311 -r_ = 1e-6 * 512 -lim = T_edma(b, r_, 512) / np.log2(1 + 2 * r_ / 512) -report("V8 wideband limit", abs(lim - (1 - b**2)) < 1e-3, - f"T/C_MAC at rho/d=1e-6: {lim:.5f} vs gamma {1-b**2:.5f}") - -# ---------------- V9: beta* crossover roots ------------------------ -def beta_star(rdb, d_=512): - r_ = 10 ** (rdb / 10) - T_oma = 2 * np.log2(1 + r_ / (2 * d_)) - return brentq(lambda b: T_edma(b, r_, d_) - T_oma, 0.3, 0.9) - -b10, b20 = beta_star(10), beta_star(20) -report("V9 beta* crossover", abs(b10 - 0.700) < 5e-3 - and abs(b20 - 0.590) < 5e-3, - f"10 dB: {b10:.3f} (quoted 0.700), 20 dB: {b20:.3f} (quoted 0.590)") - -# ---------------- V10: rho_c iff-condition + value ----------------- -b = 0.311 -gg = 1 - b**2 -cb = (csi1(b, 1 + 0j) + csi2(b, 1 + 0j)) / 2 -rho_c = 512 * (2 - 1 / gg) / cb -rho_c_db = 10 * math.log10(rho_c) -eps = 1e-4 -below = T_edma(b, rho_c * (1 - eps), 512) \ - - 2 * np.log2(1 + rho_c * (1 - eps) / 1024) -above = T_edma(b, rho_c * (1 + eps), 512) \ - - 2 * np.log2(1 + rho_c * (1 + eps) / 1024) -report("V10 rho_c crossover", below > 0 > above - and abs(rho_c_db - 25.7) < 0.1, - f"rho_c {rho_c_db:.2f} dB (quoted 25.7), sign flip verified") - -# ---------------- V11: idealized-MAC crossing ---------------------- -rho_x = 2 * b**2 * 512 / gg**2 -f = lambda r_: 2 * np.log2(1 + r_ * gg / 512) - np.log2(1 + 2 * r_ / 512) -root = brentq(f, 10.0, 1e4) -report("V11 idealized crossing", abs(root / rho_x - 1) < 1e-6 - and abs(10 * math.log10(root) - 21) < 0.3, - f"root {10*math.log10(root):.2f} dB, formula 2b^2d/g^2 " - f"{10*math.log10(rho_x):.2f} dB (quoted ~21)") - -# ---------------- V12: mismatch identity + bound value ------------- -b, delta = 0.3, 0.06 -bh = b + delta -hr = 1.0 + 0j -e1, e2 = pair(64, b) -t1 = e1 + b * hr * e2 # expected-Gram surrogate outputs -t2v_ = e2 + b * np.conj(hr) * e1 -g1 = (t1 - bh * hr * t2v_) / (1 - bh**2) -lhs = g1 - e1 -rhs = delta / (1 - bh**2) * (bh * e1 - hr * e2) -dev = np.linalg.norm(lhs - rhs) -bound = delta**2 * (abs(bh) + abs(hr))**2 / (1 - bh**2)**2 -report("V12 mismatch identity", dev < 1e-12 - and abs(bound - 8.8e-3) < 2e-4, - f"identity dev {dev:.1e}, bound {bound:.4f} (quoted 8.8e-3)") - -# ---------------- V13: CSI-direction invariance -------------------- -d = 256 -b = 0.311 -g = 1 - b**2 -e1, e2 = pair(d, b) -U1, U2 = haar(d), haar(d) -M1, M2 = U1, b * U1 + math.sqrt(g) * U2 -h1, h2 = 0.7 - 0.4j, -0.2 + 1.1j -n = (rng.standard_normal(d) + 1j * rng.standard_normal(d)) / math.sqrt(2) -r = h1 * (M1 @ e1) + h2 * (M2 @ e2) + 0.1 * n -truec = (M1.T @ r / h1 - b * (h2 / h1) * (M2.T @ r / h2)) / g -csif = (M1 - b * M2).T @ r / (h1 * g) -dev1 = np.abs(truec - csif).max() -h1w = h1 * (1.5 * np.exp(0.8j)) # badly wrong estimate -wrong = (M1 - b * M2).T @ r / (h1w * g) -c_true = abs(np.vdot(truec, e1)) / (np.linalg.norm(truec)) -c_wrong = abs(np.vdot(wrong, e1)) / (np.linalg.norm(wrong)) -report("V13 CSI invariance", dev1 < 1e-12 and abs(c_true - c_wrong) < 1e-12, - f"csi-free identity dev {dev1:.1e}, |cos| unchanged " - f"({c_true:.6f} vs {c_wrong:.6f})") - -# ---------------- V14: cross-moment lemma -------------------------- -d = 256 -b = 0.311 -sig2 = 0.5 -acc = 0.0 -NT = 4000 -U1, U2 = haar(d), haar(d) -M1, M2 = U1, b * U1 + math.sqrt(1 - b**2) * U2 -for _ in range(NT): - n = math.sqrt(sig2) * (rng.standard_normal(d) - + 1j * rng.standard_normal(d)) / math.sqrt(2) - acc += np.real(np.conj(n) @ (M1 @ (M2.T @ n))) -acc /= NT -th = sig2 * b * d -report("V14 cross-moment lemma", abs(acc / th - 1) < 0.05, - f"MC {acc:.3f} vs sigma^2 beta d {th:.3f} " - f"({100*abs(acc/th-1):.1f}%)") - -# ---------------- V15: [B^-1]_uu Sherman-Morrison ------------------ -ok = True -for U in range(2, 7): - for b in (0.1, 0.311, 0.6): - B = (1 - b) * np.eye(U) + b * np.ones((U, U)) - num = 1 + (U - 2) * b - den = (1 - b) * (1 + (U - 1) * b) - if abs(np.linalg.inv(B)[0, 0] - num / den) > 1e-12: - ok = False -report("V15 [B^-1]_uu formula", ok, "U=2..6, beta grid, exact") - -# ---------------- V16: multi-user C_SI^(U) ------------------------- -d = 512 -b = 0.311 -g = 1 - b**2 -cb = (csi1(b, 1 + 0j) + csi2(b, 1 + 0j)) / 2 -for U in (3, 4): - B = (1 - b) * np.eye(U) + b * np.ones((U, U)) - Binv = np.linalg.inv(B) - es = [] - e1 = unit(rng.standard_normal(d)) - for u in range(U): - if u == 0: - es.append(e1) +# ---------------------------------------------------------------- V10 +beta = 0.3 +sig = 10 ** (-20.0 / 20.0) +base, d1, d2 = 0.0, 0.0, 0.0 +for _ in range(30): + e1, _, Q, t1, _ = run_pair(beta, sig) + for bh, tag in ((beta, "b"), (beta + 0.2, "1"), (beta + 0.4, "2")): + g1 = aware(t1, Q, bh, 1.0, sig * sig, D) + m = float(np.linalg.norm(g1 - e1) ** 2) + if tag == "b": + base += m + elif tag == "1": + d1 += m else: - w = rng.standard_normal(d) - w = unit(w - (w @ e1) * e1) - es.append(b * e1 + math.sqrt(g) * w) - mse = 0.0 - NT = 60 - for _ in range(NT): - Us = [haar(d) for _ in range(U)] - Ms = [Us[0]] - for u in range(1, U): - Ms.append(b * Us[0] + math.sqrt(g) * Us[u]) - r = sum(Ms[u] @ es[u] for u in range(U)) # h_u = 1 - t = np.stack([Ms[u].T @ r for u in range(U)]) - rec = np.einsum("uv,vd->ud", Binv, t) - mse += np.linalg.norm(rec[0] - es[0])**2 - mse /= NT - ratio = mse / ((U - 1) * cb) - report(f"V16 C_SI^(U) additivity (U={U})", abs(ratio - 1) < 0.10, - f"noise-free MSE {mse:.3f} vs (U-1)C-bar " - f"{(U-1)*cb:.3f} (ratio {ratio:.3f})") + d2 += m +base /= 30; d1 /= 30; d2 /= 30 +r_quad = (d2 - base) / max(d1 - base, 1e-12) +allok &= check("V10 quadratic mismatch (delta doubling ~ 4x)", + 2.5 < r_quad < 6.5, + f"MSE(+0)={base:.4f} MSE(+0.2)={d1:.4f} " + f"MSE(+0.4)={d2:.4f} ratio {r_quad:.2f}") -# ---------------- V17: Walsh-Hadamard masks ------------------------ -d = 256 -H = hadamard(d) / math.sqrt(d) -b = 0.311 -acc = np.zeros((d, d)) -NT = 400 -for _ in range(NT): - D1 = np.diag(rng.choice([-1.0, 1.0], d)) - D2 = np.diag(rng.choice([-1.0, 1.0], d)) - W1 = H @ D1 - W2 = b * W1 + math.sqrt(1 - b**2) * H @ D2 - acc += W1.T @ W2 / NT -orth = np.abs((H @ np.diag(rng.choice([-1.0, 1.0], d))).T - @ (H @ np.diag(rng.choice([-1.0, 1.0], d))) - @ np.ones(d) / d).max() -diag_dev = abs(np.diag(acc).mean() - b) -off = np.abs(acc - np.diag(np.diag(acc))).mean() -report("V17 WH masks", diag_dev < 0.02 and off < 0.01, - f"E[cross-Gram] diag {np.diag(acc).mean():.4f} vs beta {b}, " - f"mean |off-diag| {off:.4f}") +# ---------------------------------------------------------------- V11 +beta = 0.311 +g = 1 - beta**2 +mc = 0.0 +for _ in range(30): + e1, e2 = embed_pair(D, beta) + U1, U2 = haar(D), haar(D) + M1, M2 = U1, beta * U1 + math.sqrt(g) * U2 + r = M1 @ e1 + M2 @ e2 # noise-free -> floor + t1 = M1.T @ r + t2 = M2.T @ r + g1 = (t1 - beta * t2) / g + mc += float(np.linalg.norm(g1 - e1) ** 2) +mc /= 30 +th = 1 + 4 * beta**4 / g +allok &= check("V11 correlated-mask floor 1+4b^4/g", + abs(mc / th - 1) < 0.05, + f"MC {mc:.4f} vs {th:.4f}; aware floor " + f"{math.sqrt(g)/2:.4f} (dominated)") -print() -print("=" * 60) -print(f"RESULT: {'ALL PASS' if not FAIL else 'FAILURES: ' + ', '.join(FAIL)}") +print("\nALL CHECKS PASSED" if allok else "\nSOME CHECKS FAILED") diff --git a/data/bertvit_merged.csv b/data/bertvit_merged.csv index e5747bd..fcd2fb5 100644 --- a/data/bertvit_merged.csv +++ b/data/bertvit_merged.csv @@ -1,8 +1,14 @@ -snr_db,edma,oma,genie,att,att_x,todma,edma_ref,edma_ref2 -0.0,0.04509729548248326,0.03878942917318714,0.04510116805362887,0.06537951208185813,0.028956357115368724,0.009263779561898224,0.051216359648716556,0.05146971093667761 -5.0,0.06451154394270053,0.0507439763307109,0.06460883367368744,0.11006860490285489,0.02899092581015571,0.014985754149760718,0.08214049352367703,0.08245299246543433 -10.0,0.10340689217396296,0.07698703231946222,0.10399757263661547,0.18771051966437632,0.029096261892353006,0.03519980048035704,0.13997304338278754,0.14036958956224843 -15.0,0.17239818787681693,0.12749333352586661,0.17548648804419778,0.3057075884366212,0.029248349735797895,0.10412376981275459,0.23587541750084084,0.23652206338075601 -20.0,0.2789011314185965,0.2156999450240525,0.29264116008861335,0.4510118978738174,0.029343304376527338,0.21990683440776934,0.37242277625984427,0.37307923116081904 -25.0,0.41080176204708735,0.3530383192829985,0.45731712677712716,0.5840194131238486,0.02929760473668019,0.3162182821357606,0.5247271302425862,0.5245882651817437 -30.0,0.5301073454886365,0.5306502765434139,0.6408057261877141,0.6726967507733389,0.029179666470356736,0.3661426990593047,0.6493871028835615,0.6479461262985481 +snr_db,edma,oma,genie,todma,edma_ref,edma_ref2 +0.0,0.04467206875531701,0.0395211911102524,0.04470823034964269,0.00808752125339693,0.051258400181977776,0.051258395044569624 +2.5,0.052661077863012905,0.044328911576594694,0.05274493153032381,0.010903122079792332,0.06411670899382443,0.06411670250265161 +5.0,0.0644090429507196,0.0517221181144123,0.06458281029539649,0.0160517606871274,0.08237909885676345,0.08237909091782057 +7.5,0.08108708676882088,0.06272871624387336,0.0814530021866085,0.027431791894606004,0.1073888337527751,0.10738882329576882 +10.0,0.10398115491552744,0.07848262153333053,0.10474200704193208,0.04139025118059202,0.14060123476258013,0.14060122084221802 +12.5,0.1345052632171428,0.10035192567447666,0.13611418937449343,0.06825274948835351,0.18353674076730386,0.1835367217194289 +15.0,0.17397624759352767,0.1299769427673891,0.1774134961643722,0.11022667550692919,0.23722848522273124,0.23722846306452994 +17.5,0.22316362340701745,0.16911298831924795,0.2303875518660061,0.16441485880931841,0.30166534237214365,0.30166531551687514 +20.0,0.28155444214702585,0.21956481272354722,0.296139236476738,0.21888792063296963,0.37473849680507554,0.3747384671261534 +22.5,0.3466694929706864,0.2826504937937716,0.37431714535458016,0.27025579480204087,0.4521061575273052,0.45210612908937037 +25.0,0.41391337811248374,0.35837042205035685,0.4623712573153898,0.31270788677551076,0.5278005284816026,0.5278005079459399 +27.5,0.47757373259635644,0.4446330708428286,0.5553692922927439,0.34371633065177387,0.5959934616461396,0.5959934508893638 +30.0,0.5326515504252165,0.5369720551883802,0.6469332071393729,0.36358971142016083,0.6525999860465527,0.6525999858789145 diff --git a/data/beta_sweep_corrected.csv b/data/beta_sweep_corrected.csv index 35978b2..6a79053 100644 --- a/data/beta_sweep_corrected.csv +++ b/data/beta_sweep_corrected.csv @@ -1,199 +1,199 @@ -snr_db,beta,edma,oma,genie -10,0.0,0.054752877862610065,0.028040940629869258,0.055811993139768964 -10,0.01,0.054747350279920316,0.028040940629869258,0.055811993139768964 -10,0.02,0.054730767346147666,0.028040940629869258,0.055811993139768964 -10,0.03,0.05470312850494755,0.028040940629869258,0.055811993139768964 -10,0.04,0.05466443283160872,0.028040940629869258,0.055811993139768964 -10,0.05,0.054614679036858765,0.028040940629869258,0.055811993139768964 -10,0.06,0.05455386547218096,0.028040940629869258,0.055811993139768964 -10,0.07,0.05448199013665998,0.028040940629869258,0.055811993139768964 -10,0.08,0.05439905068534405,0.028040940629869258,0.055811993139768964 -10,0.09,0.05430504443913251,0.028040940629869258,0.055811993139768964 -10,0.1,0.054199968396186245,0.028040940629869258,0.055811993139768964 -10,0.11,0.05408381924486196,0.028040940629869258,0.055811993139768964 -10,0.12,0.053956593378173205,0.028040940629869258,0.055811993139768964 -10,0.13,0.05381828690977572,0.028040940629869258,0.055811993139768964 -10,0.14,0.053668895691484,0.028040940629869258,0.055811993139768964 -10,0.15,0.05350841533231138,0.028040940629869258,0.055811993139768964 -10,0.16,0.0533368412190459,0.028040940629869258,0.055811993139768964 -10,0.17,0.05315416853834894,0.028040940629869258,0.055811993139768964 -10,0.18,0.0529603923003942,0.028040940629869258,0.055811993139768964 -10,0.19,0.052755507364029314,0.028040940629869258,0.055811993139768964 -10,0.2,0.05253950846347685,0.028040940629869258,0.055811993139768964 -10,0.21,0.05231239023656164,0.028040940629869258,0.055811993139768964 -10,0.22,0.0520741472544757,0.028040940629869258,0.055811993139768964 -10,0.23,0.05182477405307159,0.028040940629869258,0.055811993139768964 -10,0.24,0.05156426516568283,0.028040940629869258,0.055811993139768964 -10,0.25,0.051292615157482124,0.028040940629869258,0.055811993139768964 -10,0.26,0.05100981866135644,0.028040940629869258,0.055811993139768964 -10,0.27,0.05071587041531166,0.028040940629869258,0.055811993139768964 -10,0.28,0.05041076530139947,0.028040940629869258,0.055811993139768964 -10,0.29,0.050094498386152486,0.028040940629869258,0.055811993139768964 -10,0.3,0.04976706496254393,0.028040940629869258,0.055811993139768964 -10,0.31,0.04942846059343863,0.028040940629869258,0.055811993139768964 -10,0.32,0.04907868115655627,0.028040940629869258,0.055811993139768964 -10,0.33,0.04871772289091538,0.028040940629869258,0.055811993139768964 -10,0.34,0.04834558244476425,0.028040940629869258,0.055811993139768964 -10,0.35000000000000003,0.04796225692498555,0.028040940629869258,0.055811993139768964 -10,0.36,0.04756774394795485,0.028040940629869258,0.055811993139768964 -10,0.37,0.047162041691850294,0.028040940629869258,0.055811993139768964 -10,0.38,0.04674514895039684,0.028040940629869258,0.055811993139768964 -10,0.39,0.046317065188023115,0.028040940629869258,0.055811993139768964 -10,0.4,0.04587779059641903,0.028040940629869258,0.055811993139768964 -10,0.41000000000000003,0.045427326152471775,0.028040940629869258,0.055811993139768964 -10,0.42,0.04496567367756414,0.028040940629869258,0.055811993139768964 -10,0.43,0.04449283589819911,0.028040940629869258,0.055811993139768964 -10,0.44,0.044008816507942514,0.028040940629869258,0.055811993139768964 -10,0.45,0.04351362023064087,0.028040940629869258,0.055811993139768964 -10,0.46,0.04300725288489753,0.028040940629869258,0.055811993139768964 -10,0.47000000000000003,0.04248972144976068,0.028040940629869258,0.055811993139768964 -10,0.48,0.04196103413160188,0.028040940629869258,0.055811993139768964 -10,0.49,0.041421200432142105,0.028040940629869258,0.055811993139768964 -10,0.5,0.040870231217584416,0.028040940629869258,0.055811993139768964 -10,0.51,0.04030813878880955,0.028040940629869258,0.055811993139768964 -10,0.52,0.03973493695259877,0.028040940629869258,0.055811993139768964 -10,0.53,0.039150641093819265,0.028040940629869258,0.055811993139768964 -10,0.54,0.03855526824853652,0.028040940629869258,0.055811993139768964 -10,0.55,0.03794883717799136,0.028040940629869258,0.055811993139768964 -10,0.56,0.03733136844337993,0.028040940629869258,0.055811993139768964 -10,0.5700000000000001,0.03670288448138829,0.028040940629869258,0.055811993139768964 -10,0.58,0.036063409680404356,0.028040940629869258,0.055811993139768964 -10,0.59,0.035412970457347266,0.028040940629869258,0.055811993139768964 -10,0.6,0.034751595335041165,0.028040940629869258,0.055811993139768964 -10,0.61,0.03407931502005716,0.028040940629869258,0.055811993139768964 -10,0.62,0.033396162480946456,0.028040940629869258,0.055811993139768964 -10,0.63,0.03270217302678281,0.028040940629869258,0.055811993139768964 -10,0.64,0.03199738438593123,0.028040940629869258,0.055811993139768964 -10,0.65,0.03128183678494588,0.028040940629869258,0.055811993139768964 -10,0.66,0.030555573027513647,0.028040940629869258,0.055811993139768964 -10,0.67,0.02981863857334309,0.028040940629869258,0.055811993139768964 -10,0.68,0.02907108161689106,0.028040940629869258,0.055811993139768964 -10,0.6900000000000001,0.028312953165838792,0.028040940629869258,0.055811993139768964 -10,0.7000000000000001,0.027544307119194162,0.028040940629869258,0.055811993139768964 -10,0.71,0.026765200344916605,0.028040940629869258,0.055811993139768964 -10,0.72,0.02597569275694614,0.028040940629869258,0.055811993139768964 -10,0.73,0.025175847391522434,0.028040940629869258,0.055811993139768964 -10,0.74,0.024365730482666725,0.028040940629869258,0.055811993139768964 -10,0.75,0.023545411536702542,0.028040940629869258,0.055811993139768964 -10,0.76,0.022714963405684276,0.028040940629869258,0.055811993139768964 -10,0.77,0.02187446235961152,0.028040940629869258,0.055811993139768964 -10,0.78,0.021023988157275277,0.028040940629869258,0.055811993139768964 -10,0.79,0.0201636241156152,0.028040940629869258,0.055811993139768964 -10,0.8,0.019293457177440704,0.028040940629869258,0.055811993139768964 -10,0.81,0.018413577977365713,0.028040940629869258,0.055811993139768964 -10,0.8200000000000001,0.01752408090582169,0.028040940629869258,0.055811993139768964 -10,0.8300000000000001,0.016625064170996434,0.028040940629869258,0.055811993139768964 -10,0.84,0.015716629858541303,0.028040940629869258,0.055811993139768964 -10,0.85,0.014798883988911094,0.028040940629869258,0.055811993139768964 -10,0.86,0.013871936572160262,0.028040940629869258,0.055811993139768964 -10,0.87,0.012935901660068121,0.028040940629869258,0.055811993139768964 -10,0.88,0.011990897395407239,0.028040940629869258,0.055811993139768964 -10,0.89,0.011037046058227617,0.028040940629869258,0.055811993139768964 -10,0.9,0.010074474108977324,0.028040940629869258,0.055811993139768964 -10,0.91,0.009103312228316158,0.028040940629869258,0.055811993139768964 -10,0.92,0.008123695353459515,0.028040940629869258,0.055811993139768964 -10,0.93,0.007135762710902627,0.028040940629869258,0.055811993139768964 -10,0.9400000000000001,0.006139657845366697,0.028040940629869258,0.055811993139768964 -10,0.9500000000000001,0.0051355286448144695,0.028040940629869258,0.055811993139768964 -10,0.96,0.004123527361384938,0.028040940629869258,0.055811993139768964 -10,0.97,0.0031038106281055557,0.028040940629869258,0.055811993139768964 -10,0.98,0.002076539471223253,0.028040940629869258,0.055811993139768964 -20,0.0,0.4366911765474922,0.2688526404418522,0.5147756853853035 -20,0.01,0.4366440287117167,0.2688526404418522,0.5147756853853035 -20,0.02,0.43650257394341674,0.2688526404418522,0.5147756853853035 -20,0.03,0.43626677851363305,0.2688526404418522,0.5147756853853035 -20,0.04,0.4359365863873445,0.2688526404418522,0.5147756853853035 -20,0.05,0.4355119194934469,0.2688526404418522,0.5147756853853035 -20,0.06,0.43499267810292913,0.2688526404418522,0.5147756853853035 -20,0.07,0.43437874131546467,0.2688526404418522,0.5147756853853035 -20,0.08,0.433669967654663,0.2688526404418522,0.5147756853853035 -20,0.09,0.4328661957722866,0.2688526404418522,0.5147756853853035 -20,0.1,0.4319672452617536,0.2688526404418522,0.5147756853853035 -20,0.11,0.43097291758127854,0.2688526404418522,0.5147756853853035 -20,0.12,0.4298829970870116,0.2688526404418522,0.5147756853853035 -20,0.13,0.42869725217652294,0.2688526404418522,0.5147756853853035 -20,0.14,0.42741543654296693,0.2688526404418522,0.5147756853853035 -20,0.15,0.4260372905402156,0.2688526404418522,0.5147756853853035 -20,0.16,0.424562542659194,0.2688526404418522,0.5147756853853035 -20,0.17,0.4229909111155674,0.2688526404418522,0.5147756853853035 -20,0.18,0.4213221055488418,0.2688526404418522,0.5147756853853035 -20,0.19,0.4195558288327915,0.2688526404418522,0.5147756853853035 -20,0.2,0.4176917789970004,0.2688526404418522,0.5147756853853035 -20,0.21,0.41572965125910694,0.2688526404418522,0.5147756853853035 -20,0.22,0.413669140167154,0.2688526404418522,0.5147756853853035 -20,0.23,0.411509941851192,0.2688526404418522,0.5147756853853035 -20,0.24,0.40925175638303946,0.2688526404418522,0.5147756853853035 -20,0.25,0.40689429024279106,0.2688526404418522,0.5147756853853035 -20,0.26,0.40443725889034393,0.2688526404418522,0.5147756853853035 -20,0.27,0.40188038943985416,0.2688526404418522,0.5147756853853035 -20,0.28,0.399223423434633,0.2688526404418522,0.5147756853853035 -20,0.29,0.39646611971957335,0.2688526404418522,0.5147756853853035 -20,0.3,0.3936082574077306,0.2688526404418522,0.5147756853853035 -20,0.31,0.3906496389371964,0.2688526404418522,0.5147756853853035 -20,0.32,0.3875900932138665,0.2688526404418522,0.5147756853853035 -20,0.33,0.38442947883515566,0.2688526404418522,0.5147756853853035 -20,0.34,0.3811676873891227,0.2688526404418522,0.5147756853853035 -20,0.35000000000000003,0.37780464682285725,0.2688526404418522,0.5147756853853035 -20,0.36,0.3743403248733226,0.2688526404418522,0.5147756853853035 -20,0.37,0.3707747325532069,0.2688526404418522,0.5147756853853035 -20,0.38,0.36710792768362743,0.2688526404418522,0.5147756853853035 -20,0.39,0.36334001846484065,0.2688526404418522,0.5147756853853035 -20,0.4,0.35947116707538496,0.2688526404418522,0.5147756853853035 -20,0.41000000000000003,0.35550159328936487,0.2688526404418522,0.5147756853853035 -20,0.42,0.3514315781008482,0.2688526404418522,0.5147756853853035 -20,0.43,0.3472614673436184,0.2688526404418522,0.5147756853853035 -20,0.44,0.3429916752938002,0.2688526404418522,0.5147756853853035 -20,0.45,0.3386226882421745,0.2688526404418522,0.5147756853853035 -20,0.46,0.3341550680222958,0.2688526404418522,0.5147756853853035 -20,0.47000000000000003,0.32958945547987767,0.2688526404418522,0.5147756853853035 -20,0.48,0.3249265738682723,0.2688526404418522,0.5147756853853035 -20,0.49,0.32016723215429327,0.2688526404418522,0.5147756853853035 -20,0.5,0.3153123282180963,0.2688526404418522,0.5147756853853035 -20,0.51,0.3103628519303606,0.2688526404418522,0.5147756853853035 -20,0.52,0.3053198880896007,0.2688526404418522,0.5147756853853035 -20,0.53,0.3001846192021282,0.2688526404418522,0.5147756853853035 -20,0.54,0.2949583280869223,0.2688526404418522,0.5147756853853035 -20,0.55,0.2896424002875374,0.2688526404418522,0.5147756853853035 -20,0.56,0.2842383262731147,0.2688526404418522,0.5147756853853035 -20,0.5700000000000001,0.27874770341065014,0.2688526404418522,0.5147756853853035 -20,0.58,0.27317223769083826,0.2688526404418522,0.5147756853853035 -20,0.59,0.2675137451901349,0.2688526404418522,0.5147756853853035 -20,0.6,0.26177415325210973,0.2688526404418522,0.5147756853853035 -20,0.61,0.25595550137174955,0.2688526404418522,0.5147756853853035 -20,0.62,0.2500599417670772,0.2688526404418522,0.5147756853853035 -20,0.63,0.24408973962332856,0.2688526404418522,0.5147756853853035 -20,0.64,0.238047272995916,0.2688526404418522,0.5147756853853035 -20,0.65,0.23193503235958426,0.2688526404418522,0.5147756853853035 -20,0.66,0.22575561979243286,0.2688526404418522,0.5147756853853035 -20,0.67,0.21951174778494328,0.2688526404418522,0.5147756853853035 -20,0.68,0.21320623766571534,0.2688526404418522,0.5147756853853035 -20,0.6900000000000001,0.20684201763731608,0.2688526404418522,0.5147756853853035 -20,0.7000000000000001,0.20042212041749688,0.2688526404418522,0.5147756853853035 -20,0.71,0.1939496804829589,0.2688526404418522,0.5147756853853035 -20,0.72,0.18742793091491083,0.2688526404418522,0.5147756853853035 -20,0.73,0.1808601998477949,0.2688526404418522,0.5147756853853035 -20,0.74,0.17424990652476743,0.2688526404418522,0.5147756853853035 -20,0.75,0.16760055696580017,0.2688526404418522,0.5147756853853035 -20,0.76,0.16091573925657646,0.2688526404418522,0.5147756853853035 -20,0.77,0.15419911846869444,0.2688526404418522,0.5147756853853035 -20,0.78,0.147454431224025,0.2688526404418522,0.5147756853853035 -20,0.79,0.14068547991839878,0.2688526404418522,0.5147756853853035 -20,0.8,0.13389612662207573,0.2688526404418522,0.5147756853853035 -20,0.81,0.12709028667666042,0.2688526404418522,0.5147756853853035 -20,0.8200000000000001,0.12027192201028448,0.2688526404418522,0.5147756853853035 -20,0.8300000000000001,0.11344503419488466,0.2688526404418522,0.5147756853853035 -20,0.84,0.10661365727133493,0.2688526404418522,0.5147756853853035 -20,0.85,0.0997818503699352,0.2688526404418522,0.5147756853853035 -20,0.86,0.09295369015536699,0.2688526404418522,0.5147756853853035 -20,0.87,0.0861332631266301,0.2688526404418522,0.5147756853853035 -20,0.88,0.07932465780369294,0.2688526404418522,0.5147756853853035 -20,0.89,0.07253195683359023,0.2688526404418522,0.5147756853853035 -20,0.9,0.06575922904947452,0.2688526404418522,0.5147756853853035 -20,0.91,0.059010521516665373,0.2688526404418522,0.5147756853853035 -20,0.92,0.052289851600038885,0.2688526404418522,0.5147756853853035 -20,0.93,0.04560119908714468,0.2688526404418522,0.5147756853853035 -20,0.9400000000000001,0.03894849840124009,0.2688526404418522,0.5147756853853035 -20,0.9500000000000001,0.03233563093797358,0.2688526404418522,0.5147756853853035 -20,0.96,0.025766417558755906,0.2688526404418522,0.5147756853853035 -20,0.97,0.019244611272917996,0.2688526404418522,0.5147756853853035 -20,0.98,0.012773890139589456,0.2688526404418522,0.5147756853853035 +snr_db,beta,edma,blind,oma,genie +10,0.0,0.054752877862610065,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.01,0.05475820153789551,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.02,0.05477417262711213,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.03,0.054800791320353745,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.04,0.054838057934443386,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.05,0.05488597291295555,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.06,0.054944536826228706,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.07,0.055013750371386755,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.08,0.05509361437236641,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.09,0.055184129779950464,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.1,0.05528529767180255,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.11,0.05539711925250827,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.12,0.055519595853624266,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.13,0.05565272893372537,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.14,0.055796520078463425,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.15,0.05595097100062803,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.16,0.056116083540211255,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.17,0.05629185966448005,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.18,0.05647830146804995,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.19,0.056675411172967774,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.2,0.056883191128795564,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.21,0.05710164381270159,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.22,0.0573307718295557,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.23,0.057570577912030435,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.24,0.05782106492070458,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.25,0.058082235844175524,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.26,0.05835409379917405,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.27,0.058636642030681585,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.28,0.058929883912060506,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.29,0.059233822945179315,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.3,0.059548462760553325,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.31,0.05987380711747824,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.32,0.0602098599041811,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.33,0.06055662513796847,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.34,0.060914106965381805,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.35000000000000003,0.061282309662356316,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.36,0.06166123763439134,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.37,0.06205089541671533,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.38,0.06245128767446481,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.39,0.06286241920286531,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.4,0.06328429492741498,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.41000000000000003,0.0637169199040789,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.42,0.06416029931948275,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.43,0.0646144384911171,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.44,0.06507934286754048,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.45,0.06555501802859463,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.46,0.0660414696856211,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.47000000000000003,0.06653870368168054,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.48,0.06704672599178685,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.49,0.06756554272313225,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.5,0.06809516011533256,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.51,0.06863558454066575,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.52,0.06918682250432519,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.53,0.06974888064467029,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.54,0.07032176573348846,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.55,0.07090548467626023,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.56,0.07150004451242854,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.5700000000000001,0.0721054524156766,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.58,0.07272171569420816,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.59,0.07334884179103472,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.6,0.07398683828426894,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.61,0.07463571288742164,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.62,0.07529547344970607,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.63,0.07596612795634762,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.64,0.07664768452889742,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.65,0.07734015142555628,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.66,0.07804353704149611,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.67,0.07875784990919524,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.68,0.07948309869877611,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.6900000000000001,0.08021929221834728,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.7000000000000001,0.08096643941435333,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.71,0.08172454937193066,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.72,0.08249363131526735,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.73,0.08327369460797196,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.74,0.08406474875344346,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.75,0.08486680339525016,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.76,0.08567986831752004,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.77,0.08650395344532251,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.78,0.08733906884507195,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.79,0.08818522472492742,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.8,0.08904243143520407,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.81,0.08991069946878359,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.8200000000000001,0.09079003946154299,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.8300000000000001,0.09168046219277223,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.84,0.09258197858561873,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.85,0.0934945997075168,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.86,0.09441833677064365,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.87,0.09535320113236508,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.88,0.09629920429569758,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.89,0.0972563579097737,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.9,0.09822467377031467,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.91,0.09920416382010676,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.92,0.100194840149489,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.93,0.10119671499684378,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.9400000000000001,0.10220980074909554,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.9500000000000001,0.1032341099422143,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.96,0.104269655261731,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.97,0.10531644954325302,0.054752877862610065,0.028040940629869258,0.055811993139768964 +10,0.98,0.10637450577298989,0.054752877862610065,0.028040940629869258,0.055811993139768964 +20,0.0,0.4366911765474922,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.01,0.43672694031779324,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.02,0.4368342357122561,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.03,0.43701307498298886,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.04,0.4372634785549242,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.05,0.43758547503295236,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.06,0.43797910121191785,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.07,0.4384444020894803,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.08,0.4389814308818518,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.09,0.4395902490424265,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.1,0.44027092628330783,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.11,0.44102354059974963,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.12,0.441848178297537,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.13,0.44274493402331877,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.14,0.44371391079791206,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.15,0.44475522005260776,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.16,0.4458689816685024,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.17,0.4470553240188804,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.18,0.4483143840146794,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.19,0.4496463071530782,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.2,0.4510512475692266,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.21,0.45252936809117394,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.22,0.4540808402980178,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.23,0.4557058445813321,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.24,0.4574045702099053,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.25,0.4591772153978466,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.26,0.461023987376106,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.27,0.46294510246745635,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.28,0.46494078616500706,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.29,0.46701127321428854,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.3,0.4691568076989885,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.31,0.4713776431303909,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.32,0.4736740425405888,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.33,0.4760462785795442,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.34,0.4784946336160657,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.35000000000000003,0.4810193998427861,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.36,0.4836208793852107,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.37,0.48629938441493437,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.38,0.4890552372671043,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.39,0.4918887705622219,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.4,0.49480032733238916,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.41000000000000003,0.49779026115208486,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.42,0.5008589362735847,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.43,0.5040067277671271,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.44,0.5072340216659497,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.45,0.5105412151162935,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.46,0.5139287165325166,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.47000000000000003,0.517396945757436,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.48,0.5209463342280231,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.49,0.5245773251466092,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.5,0.5282903736577237,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.51,0.5320859470307273,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.52,0.5359645248483909,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.53,0.539926599201581,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.54,0.5439726748902178,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.55,0.5481032696306882,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.56,0.5523189142698807,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.5700000000000001,0.5566201530060465,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.58,0.5610075436166748,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.59,0.5654816576935766,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.6,0.570043080885417,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.61,0.5746924131478852,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.62,0.5794302690017478,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.63,0.5842572777990267,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.64,0.5891740839975437,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.65,0.5941813474440859,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.66,0.5992797436664702,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.67,0.6044699641747866,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.68,0.609752716772098,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.6900000000000001,0.6151287258749181,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.7000000000000001,0.6205987328437688,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.71,0.6261634963241505,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.72,0.6318237925982698,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.73,0.6375804159478764,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.74,0.643434179028581,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.75,0.6493859132560444,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.76,0.6554364692044342,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.77,0.6615867170175782,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.78,0.667837546833235,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.79,0.6741898692209654,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.8,0.6806446156340422,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.81,0.6872027388759366,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.8200000000000001,0.6938652135818649,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.8300000000000001,0.7006330367159468,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.84,0.707507228084549,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.85,0.7144888308663817,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.86,0.7215789121599784,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.87,0.7287785635491948,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.88,0.7360889016873875,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.89,0.7435110689009868,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.9,0.7510462338131872,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.91,0.7586955919885129,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.92,0.7664603665990705,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.93,0.7743418091133101,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.9400000000000001,0.7823412000081834,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.9500000000000001,0.7904598495055949,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.96,0.7986990983341251,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.97,0.807060318516999,0.4366911765474922,0.2688526404418522,0.5147756853853035 +20,0.98,0.8155449141873803,0.4366911765474922,0.2688526404418522,0.5147756853853035 diff --git a/data/cosine_ceiling.csv b/data/cosine_ceiling.csv new file mode 100644 index 0000000..17bbe36 --- /dev/null +++ b/data/cosine_ceiling.csv @@ -0,0 +1,3 @@ +beta,aware_mc,aware_pred,blind_mc,blind_pred +0.311,0.7251058104634285,0.7244273905766959,0.7074751788377762,0.7071067811865476 +0.8,0.8371337348222733,0.8366600265340756,0.7081796124577522,0.7071067811865476 diff --git a/data/csi_error.csv b/data/csi_error.csv index 083ca84..e2f5d69 100644 --- a/data/csi_error.csv +++ b/data/csi_error.csv @@ -1,8 +1,8 @@ sigma_h2,edma,sic -0.0,0.5445012038424076,0.5460929325736039 -0.01,0.5445012038424075,0.5432828884753359 -0.02,0.5445012038424075,0.5408883972120776 -0.05,0.5445012038424076,0.536040175102482 -0.1,0.5445012038424076,0.5277163316448914 -0.2,0.5445012038424075,0.5169782457463995 -0.3,0.5445012038424076,0.5125870168901699 +0.0,0.5985892848856748,0.5827705618180334 +0.01,0.5970788184367121,0.5824256975366734 +0.02,0.5956342787574976,0.5820832186681218 +0.05,0.5913892493443563,0.5813290306786075 +0.1,0.5849188896268607,0.5799057619913947 +0.2,0.5753242927324027,0.5776647866773419 +0.3,0.5690016979491338,0.5756815607612953 diff --git a/data/floor_validation.csv b/data/floor_validation.csv index 2af3d95..c660659 100644 --- a/data/floor_validation.csv +++ b/data/floor_validation.csv @@ -1,35 +1,35 @@ -d,snr_db,mse_mc,mse_theory,mse_ideal -256,0.0,283.9142603214685,284.4533071258869,283.41188049318094 -256,2.5,160.10089036917105,160.41563908393022,159.37421245122425 -256,5.0,90.47776215634474,90.66413246369561,89.62270583098966 -256,7.5,51.32748447743017,51.439977796849504,50.398551164143555 -256,10.0,29.312928645832216,29.38261468202405,28.3411880493181 -256,12.5,16.934181412329476,16.978847877828375,15.937421245122422 -256,15.0,9.973810638369487,10.00369721580492,8.962270583098967 -256,17.5,6.060239200519523,6.081281749120308,5.039855116414356 -256,20.0,3.85987571082472,3.8755454376377614,2.8341188049318093 -256,22.5,2.622819942039895,2.6351687572181945,1.5937421245122423 -256,25.0,1.9273969939302285,1.9376536910158488,0.8962270583098966 -256,27.5,1.5365003821408372,1.5454121443473878,0.5039855116414356 -256,30.0,1.3168093834480725,1.3248385131991331,0.28341188049318095 -256,32.5,1.1933627827422904,1.2008008451571766,0.15937421245122424 -256,35.0,1.1240146926635879,1.131049338536942,0.08962270583098966 -256,37.5,1.085070664488797,1.0918251838700959,0.05039855116414356 -256,40.0,1.0632107739660197,1.0697678207552703,0.028341188049318098 -768,0.0,853.4623064741292,851.2770681122488,850.2356414795429 -768,2.5,480.3951698733645,479.1640639863787,478.12263735367276 -768,5.0,270.60393697803266,269.9095441256749,268.86811749296896 -768,7.5,152.62953339008442,152.23708012513663,151.19565349243067 -768,10.0,86.28755819977675,86.06499078066025,85.0235641479543 -768,12.5,48.98065441173488,48.85369036807322,47.81226373536727 -768,15.0,28.001388546341435,27.928238382002856,26.886811749296903 -768,17.5,16.203841270734593,16.160991981949017,15.119565349243066 -768,20.0,9.569563575405624,9.543783047501382,8.502356414795429 -768,22.5,5.838813072859685,5.822653006242679,4.781226373536727 -768,25.0,3.7408413998745558,3.730107807635642,2.6886811749296897 -768,27.5,2.5610528622492597,2.553383167630259,1.5119565349243067 -768,30.0,1.8975997387446928,1.8916622741854952,0.8502356414795429 -768,32.5,1.5245056756935615,1.519549270059625,0.4781226373536727 -768,35.0,1.314694250809021,1.3102947501989213,0.268868117492969 -768,37.5,1.1967047053652895,1.1926222861983828,0.15119565349243066 -768,40.0,1.1303513753850125,1.1264501968539065,0.0850235641479543 +d,snr_db,mse_mc,mse_theory,mse_blind +256,0.0,0.9958906187265965,0.9957520371754321,0.9961240310077519 +256,2.5,0.9926509086327597,0.9924951337006896,0.993148778755109 +256,5.0,0.9869671196373861,0.9868069561728815,0.9879451709874938 +256,7.5,0.9771456527581006,0.9770064578879146,0.9789579793127183 +256,10.0,0.9605855179298334,0.9605076750198117,0.9637681159420289 +256,12.5,0.9337484810109989,0.9337823722702523,0.9390092836053628 +256,15.0,0.8928736458420425,0.8930612771894957,0.9009452871484813 +256,17.5,0.8360494261292735,0.8363998816883345,0.8473840548704382 +256,20.0,0.7661668636283875,0.7666566714783413,0.7807017543859648 +256,22.5,0.691947677239615,0.6925661718278007,0.709267994905624 +256,25.0,0.6243632968967908,0.6251571720537481,0.6440702380534542 +256,27.5,0.5709153641061163,0.571972710197798,0.5927077630509127 +256,30.0,0.5331897043219835,0.53458472828743,0.5567375886524822 +256,32.5,0.5086536205122679,0.5104122867933932,0.5335732525163734 +256,35.0,0.4935273743565432,0.4956303135714446,0.5194512459464362 +256,37.5,0.4844975136532381,0.48689966319840416,0.5111276997838433 +256,40.0,0.4792009242590312,0.48184935117050987,0.5063191153238548 +768,0.0,0.9986765012793098,0.9985760128125698,0.9987012987012988 +768,2.5,0.9976163216681129,0.9974733083167204,0.9976952053744985 +768,5.0,0.9957307732307438,0.9955242936982399,0.9959160824250755 +768,7.5,0.9923981843904192,0.992095537514675,0.9927835275542491 +768,10.0,0.9865629253484912,0.9861129415585025,0.9873096446700508 +768,12.5,0.9764985106901773,0.9758221660225336,0.9778701398348755 +768,15.0,0.9595662490049409,0.9585462259188507,0.9619573584726929 +768,17.5,0.9322122252329521,0.9306888249982047,0.9361315623151921 +768,20.0,0.8907470407123728,0.8885385986762078,0.8966942148760331 +768,22.5,0.8334967999962878,0.8304680517855016,0.8417416365176289 +768,25.0,0.7637208092945489,0.759888721537542,0.7741964962231777 +768,27.5,0.6903787748107578,0.6859650675890866,0.7028866275721191 +768,30.0,0.6242938905723492,0.6196325851274082,0.638728323699422 +768,32.5,0.5725413154691661,0.5679130695479454,0.5887951848908111 +768,35.0,0.5363309035044117,0.5318795970416631,0.554141276684715 +768,37.5,0.5129687106701781,0.5087260331425658,0.5319605114462806 +768,40.0,0.498680130948561,0.4946227489687355,0.5184899845916795 diff --git a/data/mask_family_rev.csv b/data/mask_family_rev.csv index 85d8881..7b35106 100644 --- a/data/mask_family_rev.csv +++ b/data/mask_family_rev.csv @@ -1,10 +1,10 @@ -snr_db,haar,wh -0,0.05647200954133838,0.05310404690708958 -5,0.08330434028403662,0.07868202035026188 -10,0.1362057604122267,0.1321506210735374 -15,0.22743813806699198,0.22553342842896307 -20,0.3620849406777639,0.36807222557624214 -25,0.5112067973087223,0.5367516728593701 -30,0.6187704792155053,0.6693336605430739 -35,0.670208963249346,0.7373274649462636 -40,0.689340654075078,0.7636921461142663 +snr_db,haar,wh,wh_exact_mse +0,0.056410143594257535,0.055201172281522305,0.9978637042641639 +5,0.085697923428379,0.08462585555389524,0.9933106958866119 +10,0.14458728155121206,0.14321016235277056,0.9794808036088943 +15,0.24451895691454412,0.2423005321621895,0.9407034531235695 +20,0.3882001306116581,0.3829878903925419,0.8522246733307839 +25,0.540928793400526,0.5299757443368435,0.7177656385302543 +30,0.6472152987122536,0.631390101313591,0.5997132909297943 +35,0.6973008319735527,0.6793428674340248,0.5366590532660485 +40,0.7159504926204682,0.6972508707642555,0.5119321745634079 diff --git a/data/mismatch.csv b/data/mismatch.csv new file mode 100644 index 0000000..fec7a72 --- /dev/null +++ b/data/mismatch.csv @@ -0,0 +1,8 @@ +beta_hat,cosine,mse +0.211,0.3896048231422901,0.8489142748713493 +0.251,0.3904690830409527,0.8482141560316085 +0.311,0.3909398098289967,0.8478092220425606 +0.371,0.3904550792276859,0.8481874433159828 +0.41100000000000003,0.38962371706962584,0.8488810566067696 +0.31490625,0.3909368622303009,0.8478098925948143 +0.0,0.37743803575634954,0.858243175148964 diff --git a/data/multiuser_corrected.csv b/data/multiuser_corrected.csv index 2ddecf8..a357d0c 100644 --- a/data/multiuser_corrected.csv +++ b/data/multiuser_corrected.csv @@ -1,40 +1,40 @@ -U,snr_db,edma,oma -2,0.0,0.005074924495401339,0.0028163887856167778 -2,2.5,0.00900326092256961,0.005006425437490073 -2,5.0,0.015943181620258408,0.008896821012114422 -2,7.5,0.028141662406857754,0.015802100105602065 -2,10.0,0.04939420068532773,0.028040940629869258 -2,12.5,0.08585687636915751,0.04967759953004671 -2,15.0,0.14680246472845263,0.08775733805059951 -2,17.5,0.24437407492333546,0.15425664820473364 -2,20.0,0.39036168250847997,0.2688526404418522 -2,22.5,0.5882403211652596,0.46202930580118473 -2,25.0,0.8236174091640396,0.7765249721523131 -2,27.5,1.0639633374340793,1.2629750132730075 -2,30.0,1.2740371164829405,1.9659871493886203 -3,0.0,0.007166121727981404,0.002816846908845676 -3,2.5,0.012688743558228709,0.005007872928745025 -3,5.0,0.022393454021796717,0.00890139152557443 -3,7.5,0.03929335948023371,0.015816514922095765 -3,10.0,0.0682640356183996,0.028086309611073952 -3,12.5,0.11661479903052363,0.04981987507531682 -3,15.0,0.19381754610825597,0.08820067087182441 -3,17.5,0.3087986357540208,0.15562283620381148 -3,20.0,0.46347315369044484,0.27298359666177824 -3,22.5,0.6453865207846086,0.4741321857975819 -3,25.0,0.8284280872854691,0.8102498992888099 -3,27.5,0.9858586691638921,1.3502134677042328 -3,30.0,1.1039774302731393,2.1701295882547527 -4,0.0,0.009176956996801149,0.002817076044986573 -4,2.5,0.01621312884680335,0.005008597092951442 -4,5.0,0.028502070980983823,0.008903679130992838 -4,7.5,0.04967547887822116,0.015823735486660634 -4,10.0,0.08531654320798115,0.028109067575874017 -4,12.5,0.14302271741867906,0.04989142100177373 -4,15.0,0.2308213082888609,0.08842458349643963 -4,17.5,0.3525340922014516,0.15631809220986412 -4,20.0,0.5011822600084102,0.27511311194165045 -4,22.5,0.6570342137422986,0.4805054388299137 -4,25.0,0.7963620296794736,0.8286132554549862 -4,27.5,0.9042441392584162,1.4000909956579182 -4,30.0,0.9788428109037877,2.294588749973288 +U,snr_db,edma_mc,blind,oma,mse_mc +2,0.0,0.0067459622753358985,0.005619067173648297,0.0028163887856167778,0.9976647585630417 +2,2.5,0.011675481754301935,0.009969612776119413,0.005006425437490073,0.9959617620706558 +2,5.0,0.020286535908117426,0.017657570045582938,0.008896821012114422,0.9929938805103302 +2,7.5,0.035291135414103295,0.03117756291292608,0.015802100105602065,0.9878435188531876 +2,10.0,0.06125380524795852,0.054752877862610065,0.028040940629869258,0.978994796872139 +2,12.5,0.10554787426027339,0.09526158699989311,0.04967759953004671,0.9640808624029159 +2,15.0,0.17922846076711016,0.16314404591732876,0.08775733805059951,0.9397740066051483 +2,17.5,0.29671520331202866,0.27228552723458505,0.15425664820473364,0.9022770518064499 +2,20.0,0.47207515868714434,0.4366911765474922,0.2688526404418522,0.8490741294622421 +2,22.5,0.7100570759041679,0.6617728169555368,0.46202930580118473,0.7818541771173477 +2,25.0,0.9952787609812811,0.9331238669104754,0.7765249721523131,0.708264736533165 +2,27.5,1.290911785811443,1.2146443868700316,1.2629750132730075,0.639290742278099 +2,30.0,1.5545191724506118,1.4647566493411088,1.9659871493886203,0.5834740561246872 +3,0.0,0.008221550750832831,0.008412218630107837,0.002816846908845676,0.9981022214889527 +3,2.5,0.015313679039211471,0.014902926454674073,0.005007872928745025,0.9964680409431458 +3,5.0,0.028029675597543307,0.026325253299532562,0.00890139152557443,0.9935446953773499 +3,7.5,0.050514313628626004,0.04626641612972433,0.015816514922095765,0.988396560549736 +3,10.0,0.08952942310946198,0.08059981755804599,0.028086309611073952,0.9795267921686173 +3,12.5,0.15534005610649262,0.13832515321163041,0.04981987507531682,0.9647452771663666 +3,15.0,0.2615951480089572,0.2316168228614546,0.08820067087182441,0.9413490122556687 +3,17.5,0.4221435294251369,0.3731878021467334,0.15562283620381148,0.9070698082447052 +3,20.0,0.6428146293469728,0.5688074586563354,0.27298359666177824,0.8619812881946564 +3,22.5,0.9107931284704225,0.8069275801266587,0.4741321857975819,0.8102293717861175 +3,25.0,1.192151455674711,1.0559460071626627,0.8102498992888099,0.7592338293790817 +3,27.5,1.4460078396289984,1.2782661617838726,1.3502134677042328,0.7159830737113952 +3,30.0,1.6453250009668556,1.4503691419311937,2.1701295882547527,0.6837582939863205 +4,0.0,0.015142645594341335,0.011194533410694072,0.002817076044986573,0.9973794192075729 +4,2.5,0.02657168051302456,0.01980238285260359,0.005008597092951442,0.9954060631990432 +4,5.0,0.04649602868391863,0.03488813263113353,0.008903679130992838,0.9919752240180969 +4,7.5,0.0808285062062145,0.0610360844265235,0.015823735486660634,0.9860911220312119 +4,10.0,0.1387823700129495,0.10550166169718982,0.028109067575874017,0.9762377244234085 +4,12.5,0.23327828100624443,0.17872115676402342,0.04989142100177373,0.9603821069002152 +4,15.0,0.3790651258653871,0.29313196378582485,0.08842458349643963,0.9364239370822907 +4,17.5,0.5861777540534772,0.458064715640174,0.15631809220986412,0.9034117364883423 +4,20.0,0.8490808651568857,0.6702163879182615,0.27511311194165045,0.8631778705120087 +4,22.5,1.1397156040760281,0.9064855445133952,0.48050543882991376,0.8207820576429367 +4,25.0,1.4160565809869674,1.1309122590875245,0.8286132554549861,0.7824041104316711 +4,27.5,1.6433747432527392,1.3140736832668964,1.4000909956579182,0.752183369398117 +4,30.0,1.808776977801607,1.4458968914537418,2.294588749973288,0.7309303051233291 diff --git a/data/rate_corrected.csv b/data/rate_corrected.csv index d907553..386c93c 100644 --- a/data/rate_corrected.csv +++ b/data/rate_corrected.csv @@ -1,32 +1,83 @@ -snr_db,edma,edma_ideal,oma,genie,mac -0.0,0.005074922224889888,0.005085968590195728,0.0028163887856167778,0.0056300312141080765,0.005624549193878107 -1.0,0.006383905773418096,0.00640139527831988,0.003545175584028836,0.007086000682366226,0.007077321020140645 -2.0,0.008028883565814418,0.008056567110301317,0.004462402142875826,0.008917913609859949,0.008904174704705635 -3.0,0.010095150934059762,0.010138955836487416,0.005616707528960678,0.01122250279942868,0.011200762797452897 -4.0,0.012689107038928529,0.01275839279457609,0.007069235608353175,0.014121193837328306,0.014086807264803002 -5.0,0.015943159211639874,0.016052690844464874,0.008896821012114422,0.017766293841720057,0.017711931943651758 -6.0,0.02002158323181113,0.02019462550149604,0.011195969967589819,0.022348664887473,0.022262779372211397 -7.0,0.025127416528746203,0.025400572273952007,0.014087824481532864,0.028107199678235918,0.02797162021400105 -8.0,0.0315103938374857,0.03194114344476364,0.017724337286238952,0.03534046212793969,0.03512665200704765 -9.0,0.03947581013823077,0.040154209208400245,0.022295928433737136,0.044420892444668104,0.044084138042602035 -10.0,0.049393985592843845,0.05046071565802713,0.028040940629869258,0.055811993139768964,0.0552824355011896 -11.0,0.06170967643915826,0.0633837099689487,0.03525725553198444,0.07008888995333985,0.0692577754750065 -12.0,0.0769502860361166,0.07957092682607385,0.04431647029951141,0.08796257073317358,0.08666134967423425 -13.0,0.09573105092136092,0.0998211434335674,0.05568105080659391,0.11030790336846401,0.10827678933608055 -14.0,0.11875450818829247,0.12511422236562836,0.06992485602776169,0.13819516297913534,0.13503645793865676 -15.0,0.14680056444078776,0.1566442653623848,0.08775733805059951,0.17292418580683266,0.1680341158620806 -16.0,0.18070258576377174,0.19585451834583373,0.11005152164455634,0.21605933241156736,0.20853051118575192 -17.0,0.22130451408346616,0.24447152199498445,0.13787549850403957,0.26946211915160523,0.2579474779472346 -18.0,0.26939472678854465,0.3045344507753753,0.17252656142846565,0.3353166538092753,0.31784551311517 -19.0,0.3256150019555273,0.3784136758834648,0.21556617256790578,0.41614100494016715,0.38988005918955787 -20.0,0.3903482286678335,0.4688105525777229,0.2688526404418522,0.5147756853853035,0.47573343096639775 -21.0,0.4635964354662514,0.578728779073748,0.3345666611396269,0.6343391981725063,0.577022932441223 -22.0,0.5448699198657065,0.7114072599088519,0.415222874482676,0.7781410805176758,0.6951911159367629 -23.0,0.6331155105059729,0.8702063713693562,0.5136586334498267,0.949546269308418,0.8313903238477779 -24.0,0.7267127428215634,1.0584449667589202,0.6329899409936198,1.1517917856752415,0.9863786311250137 -25.0,0.8235572044540542,1.279194773017329,0.7765249721523131,1.3877675181776066,1.160445692544041 -26.0,0.9212304756826651,1.535050886559065,0.9476289515595221,1.6597852620892968,1.3533832196252402 -27.0,1.0172315400901657,1.8279087143109145,1.1495412900125348,1.9693701773560432,1.5645062768096216 -28.0,1.1092250066402953,2.158784275832778,1.385156635171699,2.3171116947925556,1.7927209139328206 -29.0,1.1952558858426987,2.5277120709526093,1.656793870330709,2.702603452084613,2.0366245350378795 -30.0,1.273891696882788,2.9337415407944656,1.9659871493886203,3.1244848484421452,2.294620748891627 +snr_db,edma,blind,oma,genie,mac +0.0,0.0061610171386374275,0.005619067173648297,0.0028163887856167778,0.0056300312141080765,0.005624549193878107 +0.5,0.00691010417674122,0.0063024511854892495,0.003159852086906414,0.006316247537633628,0.006309349361561283 +1.0,0.007749905158458553,0.007068641357914831,0.003545175584028836,0.007086000682366226,0.007077321020140645 +1.5,0.00869131183087741,0.007927592484830619,0.003977454409125839,0.007949433521018416,0.00793851300292445 +2.0,0.00974649960657886,0.008890435799551143,0.004462402142875826,0.008917913609859949,0.008904174704705635 +2.5,0.010929072665281737,0.009969612776119413,0.005006425437490073,0.010004179295885398,0.009986896036002313 +3.0,0.012254223859673589,0.011179022795476752,0.005616707528960678,0.01122250279942868,0.011200762797452897 +3.5,0.013738910514711638,0.012534185756127438,0.006301301667732704,0.012588872126580048,0.012561528941353725 +4.0,0.01540204717058254,0.014052420692277471,0.007069235608353175,0.014121193837328306,0.014086807264803002 +4.5,0.017264716236363182,0.015753041409356024,0.007930628420075834,0.015839518874396368,0.015796280141876264 +5.0,0.019350397380995952,0.017657570045582938,0.008896821012114422,0.017766293841720057,0.017711931943651758 +5.5,0.02168521627394186,0.01978996930424075,0.009980521909443284,0.019926640307263106,0.019858304805751847 +6.0,0.024298212980174518,0.022176893856949428,0.011195969967589819,0.022348664887473,0.022262779372211397 +6.5,0.02722162988947048,0.024847961072149566,0.012559115877317157,0.025063803043938227,0.02495588205804394 +7.0,0.030491218490760343,0.027836040749765856,0.014087824481532864,0.028107199678235918,0.02797162021400105 +7.5,0.0341465635567491,0.03117756291292608,0.015802100105602065,0.03151812973760773,0.03134784632526704 +8.0,0.03823142234629086,0.034912841886155954,0.017724337286238952,0.03534046212793969,0.03512665200704765 +8.5,0.04279407522246475,0.03908641383789467,0.019879599469816586,0.039623170253689395,0.03935479204579221 +9.0,0.04788768258092579,0.043747383640535986,0.022295928433737136,0.044420892444668104,0.044084138042602035 +9.5,0.053570641143065464,0.04894977525797977,0.02500468735797887,0.049794545359324184,0.049372160308651776 +10.0,0.05990693045153535,0.054752877862610065,0.028040940629869258,0.055811993139768964,0.0552824355011896 +10.5,0.0669664377781569,0.061221577467185394,0.03144387359248828,0.06254877459459882,0.06188517603089207 +11.0,0.0748252465960363,0.06842666099667345,0.03525725553198444,0.07008888995333985,0.0692577754750065 +11.5,0.08356587028013787,0.07644507639991947,0.03952994922373971,0.07852564771453559,0.07748536205722763 +12.0,0.09327740881804818,0.085360128615295,0.04431647029951141,0.08796257073317358,0.08666134967423425 +12.5,0.1040556021156975,0.09526158699989311,0.04967759953004671,0.09851435888929258,0.09688797294459299 +13.0,0.11600274911459842,0.10624567530369669,0.05568105080659391,0.11030790336846401,0.10827678933608055 +13.5,0.12922745762077448,0.11841491058649742,0.06240219710982095,0.12348334468228564,0.12094912763439147 +14.0,0.14384418580218838,0.13187775289817832,0.06992485602776169,0.13819516297913534,0.13503645793865676 +14.5,0.15997253317558566,0.14674802345073423,0.07834213536920213,0.15461328486468107,0.15068065415770782 +15.0,0.17773623713261566,0.16314404591732876,0.08775733805059951,0.17292418580683266,0.1680341158620806 +15.5,0.19726183132739222,0.18118746406341277,0.09828492364037603,0.19333196120884794,0.18725971263613045 +16.0,0.2186769253440418,0.2010016899599364,0.11005152164455634,0.21605933241156736,0.20853051118575192 +16.5,0.24210807182326322,0.2227099414890049,0.12319698872821615,0.24134854631350017,0.23202924390125249 +17.0,0.26767819847782004,0.2464328367428638,0.13787549850403957,0.26946211915160523,0.2579474779472346 +17.5,0.29550359887341515,0.27228552723458505,0.15425664820473364,0.3006833665641031,0.286484446899344 +18.0,0.3256904979371402,0.3003743724210648,0.17252656142846565,0.3353166538092753,0.31784551311517 +18.5,0.35833123589614085,0.33079318537477825,0.19288896017297896,0.373687292568455,0.35224023897161644 +19.0,0.3935001471563058,0.36361911343894837,0.21556617256790578,0.41614100494016715,0.38988005918955787 +19.5,0.4312492471566523,0.39890825744125635,0.24080003515655363,0.46304287205402317,0.4309755647476398 +20.0,0.47160387826934685,0.4366911765474922,0.2688526404418522,0.5147756853853035,0.47573343096639775 +20.5,0.5145585022781102,0.47696846992857833,0.3000068719913712,0.5717376246277998,0.5243530472781891 +21.0,0.5600728580354036,0.5197066667099397,0.3345666611396269,0.6343391981725063,0.577022932441223 +21.5,0.608068724285634,0.5648346868062688,0.37285689185605564,0.7029994019650798,0.6339170443856742 +22.0,0.6584275350393116,0.6122411513558497,0.415222874482676,0.7781410805176758,0.6951911159367629 +22.5,0.7109890845273154,0.6617728169555368,0.46202930580118473,0.8601855102446063,0.7609791636000716 +23.0,0.765551528064256,0.713234378387138,0.5136586334498267,0.949546269308418,0.8313903238477779 +23.5,0.8218728332902462,0.7663898279130165,0.5705087483681691,1.0466225079410099,0.9065061679270134 +24.0,0.8796737646698733,0.8209654765747858,0.6329899409936198,1.1517917856752415,0.9863786311250137 +24.5,0.9386423967077436,0.8766546390433925,0.7015210764944273,1.265402692864754,1.071028665954073 +25.0,0.9984400543762295,0.9331238669104754,0.7765249721523131,1.3877675181776066,1.160445692544041 +25.5,1.0587084809543992,0.9900204972902541,0.8584229962332282,1.5191552559366022,1.2545878766134282 +26.0,1.119077943176341,1.0469811771611839,0.9476289515595221,1.6597852620892968,1.3533832196252402 +26.5,1.179175910637804,1.1036409416090471,1.0445423566829937,1.80982186122357,1.4567314014163084 +27.0,1.2386358989577517,1.1596423762632002,1.1495412900125348,1.9693701773560432,1.5645062768096216 +27.5,1.29710604997724,1.2146443868700316,1.2629750132730075,2.1384734087219526,1.676558897795992 +28.0,1.354257039699666,1.268330133073086,1.385156635171699,2.3171116947925556,1.7927209139328206 +28.5,1.409788954299457,1.3204137546758452,1.516356108640114,2.5052026381166788,1.912808196396262 +29.0,1.46343685117662,1.3706456179911464,1.656793870330709,2.702603452084613,2.0366245350378795 +29.5,1.5149748174907736,1.4188159253438317,1.8066354251478791,2.9091146169321833,2.1639652711380073 +30.0,1.5642184427865493,1.4647566493411088,1.9659871493886203,3.1244848484421452,2.294620748891627 +30.5,1.611025724734185,1.5083418630206922,2.134893534015794,3.3484171235368767,2.4283794932787846 +31.0,1.655296518212399,1.5494866278103396,2.3133360179346942,3.580575468601784,2.5650310482060616 +31.5,1.69697071073413,1.5881446672671957,2.5012334757215813,3.8205922014764093,2.7043684343718026 +32.0,1.7360253572646107,1.6243050934735594,2.6984443327894785,4.068075325449263,2.8461902094614113 +32.5,1.7724710336003184,1.657988465829612,2.9047701920540088,4.322615799931558,2.990302132880585 +33.0,1.8063476712179856,1.6892424524988017,3.1199607780022283,4.58379445293828,3.1365184527185406 +33.5,1.8377201214872863,1.7181373382163942,3.3437199433889635,4.851188349616376,3.284662843916385 +34.0,1.8666736682078067,1.7447615844356081,3.5757124449541577,5.124376483511814,3.434569033973711 +34.5,1.8933096697926761,1.7692176044671448,3.8155711791720877,5.4029447084663,3.5860811564667223 +35.0,1.917741470965885,1.7916178721312395,4.062904576010332,5.686489875468202,3.7390538737997208 +35.5,1.940090682653944,1.8120814410975492,4.317303874670602,5.974623178146912,3.8933523096222302 +36.0,1.96048389087427,1.8307309159245366,4.578350045500127,6.266972741742281,4.048851828833409 +36.5,1.9790498228022666,1.8476898860934903,4.845620171250711,6.563185513116703,4.205437699605096 +37.0,1.995916971802156,1.8630808114160524,5.118693153295247,6.862928524245812,4.363004667830932 +37.5,2.0112116632055823,1.877023330764698,5.397154659675771,7.165889609632854,4.5214564701987765 +38.0,2.025056528618942,1.8896329553932965,5.68060127841664,7.471777660490731,4.680705307942014 +38.5,2.0375693477816395,1.901020102220074,5.968643879061808,7.7803224966316336,4.840671299425853 +39.0,2.048862212555602,1.911289420316504,6.260910216711984,8.09127443203426,5.001281926175381 +39.5,2.0590409665385074,1.9205393645088147,6.557046835743057,8.404403603108598,5.162471483808707 +40.0,2.0682048751549056,1.9288619725928229,6.856720345408583,8.71949912064466,5.324180546618741 +40.5,2.076446484100768,1.936342806471171,7.1596181476676515,9.036368098013284,5.486355452242227 diff --git a/data/rayleigh_mse.csv b/data/rayleigh_mse.csv new file mode 100644 index 0000000..3ec1610 --- /dev/null +++ b/data/rayleigh_mse.csv @@ -0,0 +1,3 @@ +snr_db,aware_mean,aware_median,blind_mean,blind_median +10,0.9795477778116862,0.9844803214073181,0.9812235805193583,0.9864863753318787 +20,0.86083795551459,0.8848964273929596,0.8715663189888001,0.8971874713897705 diff --git a/data/sic_comparison.csv b/data/sic_comparison.csv index 952c2f4..0857bd2 100644 --- a/data/sic_comparison.csv +++ b/data/sic_comparison.csv @@ -1,8 +1,14 @@ -snr_db,edma,oma,genie,sic -0,0.05294439072634817,0.04744227546406637,0.05379261851424639,0.05389018816321296 -5,0.07594817329319892,0.06247051502646607,0.07880139316120491,0.07881668500992306 -10,0.12217900552930536,0.09554048483563253,0.12869653017833063,0.12780141614005225 -15,0.20186260353988977,0.15862477653551696,0.21655865548824185,0.210747933448543 -20,0.3172937611389947,0.2656884406371917,0.3525261215672996,0.32788756860888746 -25,0.44561602112223075,0.4226203227111401,0.5255728405808201,0.45184034211813306 -30,0.5477712579317904,0.6069297656726085,0.6947138682844006,0.542792955511582 +snr_db,edma,blind,oma,genie,sic +0.0,0.05578717951430008,0.05400461608078331,0.04721722166286781,0.05396917013451457,0.05426549927797168 +2.5,0.065774643314071,0.06321258225478232,0.052756696401629596,0.06320590722374618,0.06361642193980516 +5.0,0.08065654144156724,0.07687171540223062,0.06147742178989574,0.07694214591756462,0.07748856123536825 +7.5,0.10187424055300653,0.09648836613399908,0.07444818876450882,0.09673152786213905,0.09735255000414327 +10.0,0.13085299325641245,0.12340473086107523,0.0929846009076573,0.1241416653757915,0.12454899992793798 +12.5,0.16899134901352228,0.15893537403084337,0.11866934722289443,0.1608496064506471,0.1604155235271901 +15.0,0.2173447159398347,0.2041423544753343,0.15335392403416337,0.20866850532591344,0.20602014526724816 +17.5,0.27592357981950044,0.2591604423709214,0.19881520241498948,0.26905467864125965,0.2615451134555042 +20.0,0.34281544568017125,0.32233451675623653,0.25661767227575183,0.34233618564903734,0.32547976134344936 +22.5,0.4138438655436039,0.38978456068784,0.3273876936547458,0.4268909978121519,0.394242920614779 +25.0,0.4831898649036884,0.45591997236013415,0.4098757527023554,0.5186095271632075,0.462635233476758 +27.5,0.5450719533115626,0.5151139491051435,0.5004852302744984,0.6114595555514097,0.5252878930792213 +30.0,0.5955657368898392,0.5634874982386827,0.593484514914453,0.6988957175612449,0.5781986298412085 diff --git a/data/theorem_check.csv b/data/theorem_check.csv index dcc48b1..bd007fc 100644 --- a/data/theorem_check.csv +++ b/data/theorem_check.csv @@ -1,5 +1,5 @@ beta,user1_mc_over_theory,user2_mc_over_theory,max_dev_pct -0.0,1.0,1.0,0.0 -0.311,1.0007005180677906,0.9999999999999998,0.07005180677905898 -0.5,0.9964527822308402,0.9999999999999998,0.3547217769159783 -0.7,0.9981870510670983,1.0,0.18129489329017368 +0.0,1.0003971986044795,0.9997756167547166,0.03971986044795095 +0.311,1.003449173346779,1.0016398632768944,0.34491733467789665 +0.5,1.0004382526911781,0.9996516691448926,0.04382526911781426 +0.7,0.9986337430756431,0.9983924640367988,0.16075359632011788 diff --git a/fig/block_diagram.pdf b/fig/block_diagram.pdf index 195519a..92caeb4 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 new file mode 100644 index 0000000..8d1d87d --- /dev/null +++ b/fig/block_diagram_src.tex @@ -0,0 +1,66 @@ +% Standalone TikZ source for the EDMA block diagram (Fig. 1). +% Compile: latexmk -pdf block_diagram_src.tex; copy PDF to block_diagram.pdf +\documentclass[tikz,border=2pt]{standalone} +\usepackage{amsmath,amssymb,bm} +\newcommand{\mb}[1]{\mathbf{#1}} +\usetikzlibrary{arrows.meta,positioning,fit,calc} +\begin{document} +\begin{tikzpicture}[ + font=\footnotesize, + node distance=3.2mm and 4.5mm, + blk/.style={draw, semithick, minimum height=5.5mm, minimum width=9mm, + inner sep=1.5pt, align=center}, + sum/.style={draw, semithick, circle, inner sep=0pt, minimum size=3.6mm}, + arr/.style={-{Latex[length=1.6mm]}, semithick}, + dsh/.style={-{Latex[length=1.6mm]}, densely dashed, thin}, + lbl/.style={inner sep=1pt} +] +% ---------------- user 1 chain ---------------- +\node[lbl] (b1) {$b_1$}; +\node[blk, right=of b1] (pe1) {$\mathrm{PE}_1$}; +\node[blk, right=of pe1] (m1) {$\mb{M}_1$}; +\node[sum, right=7mm of m1] (h1) {$\times$}; +\node[lbl, above=1.2mm of h1] {$h_1$}; +% ---------------- user U chain ---------------- +\node[lbl, below=11mm of b1] (bU) {$b_U$}; +\node[blk, right=of bU] (peU) {$\mathrm{PE}_U$}; +\node[blk, right=of peU] (mU) {$\mb{M}_U$}; +\node[sum, right=7mm of mU] (hU) {$\times$}; +\node[lbl, below=1.2mm of hU] {$h_U$}; +% vdots between chains +\path (pe1) -- (peU) node[midway] {$\vdots$}; +\path (m1) -- (mU) node[midway] {$\vdots$}; +% ---------------- channel sum ---------------- +\path (h1) -- (hU) node[midway] (mid) {}; +\node[sum] (sig) at ($(h1)!0.5!(hU)+(11mm,0)$) {$+$}; +\node[lbl, left=3.5mm of sig] (nn) {$\mb{n}$}; +% ---------------- receiver ---------------- +\node[blk, right=5.5mm of sig, minimum height=13mm] (mf) + {matched\\ filters\\ $\mb{M}_u^{\top}/h_u$}; +\node[blk, right=5mm of mf, minimum height=13mm] (wnr) + {affinity-aware\\ MMSE\\ $\mb{W}_u(\mb{B})$}; +\node[lbl, right=4.5mm of wnr] (out) {$\hat{\mb{e}}_1,\ldots,\hat{\mb{e}}_U$}; +% ---------------- affinity measurement ---------------- +\coordinate (tap1) at ($(pe1.east)!0.8!(m1.west)$); +\coordinate (tapU) at ($(peU.east)!0.8!(mU.west)$); +\node[blk] (bm) at ($(tapU)+(3mm,-9.5mm)$) + {$B_{uv}=|\langle\mb{e}_u,\mb{e}_v\rangle|$}; +% ---------------- edges ---------------- +\draw[arr] (b1) -- (pe1); +\draw[arr] (pe1) -- node[above, lbl] {$\mb{e}_1$} (m1); +\draw[arr] (m1) -- node[above, lbl] {$\mb{x}_1$} (h1); +\draw[arr] (bU) -- (peU); +\draw[arr] (peU) -- node[above, lbl, pos=0.42] {$\mb{e}_U$} (mU); +\draw[arr] (mU) -- node[below, lbl] {$\mb{x}_U$} (hU); +\draw[arr] (h1) -| (sig); +\draw[arr] (hU) -| (sig); +\draw[arr] (nn) -- (sig); +\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); +\draw[dsh] (tap1) -- ($(tap1 |- bm.north)$); +\draw[dsh] (bm.east) -| (wnr.south); +\end{tikzpicture} +\end{document} diff --git a/fig/fig_bertvit_merged.pdf b/fig/fig_bertvit_merged.pdf index 98778b8..d9a7e58 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 5950ea5..22c9162 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 e895b51..263cc2b 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 bc3b7b6..0a2c038 100644 Binary files a/fig/fig_multiuser_corrected.pdf and b/fig/fig_multiuser_corrected.pdf differ diff --git a/fig/fig_rate_corrected.pdf b/fig/fig_rate_corrected.pdf index 3e0f32e..327f060 100644 Binary files a/fig/fig_rate_corrected.pdf and b/fig/fig_rate_corrected.pdf differ diff --git a/fig/fig_sic.pdf b/fig/fig_sic.pdf index 68c6f52..fb929df 100644 Binary files a/fig/fig_sic.pdf and b/fig/fig_sic.pdf differ