Compare commits

...
10 Commits
21 changed files with 453 additions and 35 deletions
+14 -7
View File
@@ -8,7 +8,8 @@ Reproducibility package for
This repository contains the simulation code, the raw result data, and This repository contains the simulation code, the raw result data, and
the figure files behind every numerical claim in the paper. It is the figure files behind every numerical claim in the paper. It is
private during peer review and will be made public upon publication. publicly available during peer review so that the editors and
reviewers can inspect and rerun every experiment.
The design under test: each user applies an independent Haar The design under test: each user applies an independent Haar
orthogonal mask, and the receiver runs a matched filter followed by orthogonal mask, and the receiver runs a matched filter followed by
@@ -47,9 +48,10 @@ Run the scripts from inside `code/`. All plots are rendered from
| Fig. 2 | Per-user MSE, aware vs blind floor | `revision_sims.py E1` | `floor_validation.csv` | | Fig. 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. 3 | Effective sum rate at the CLIP affinity | `revision_sims.py E7a` | `rate_corrected.csv` |
| Fig. 4 | Cosine recovery on real BERT+ViT pairs | `fig_real_merged.py`, then `refine_matched.py` | `bertvit_merged.csv` | | Fig. 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. 5 | Top-1 retrieval with recovered embeddings | `retrieval_real.py` | `retrieval_real.csv` |
| Fig. 6 | Value of the measured affinity | `revision_sims.py E7a` | `beta_sweep_corrected.csv` | | Fig. 6 | Receiver comparison under Rayleigh fading | `revision_sims_gpu.py E2` | `sic_comparison.csv` |
| Fig. 7 | Multi-user scaling (joint Wiener) | `revision_sims_gpu.py E7c` | `multiuser_corrected.csv` | | Fig. 7 | Value of the measured affinity | `revision_sims.py E7a` | `beta_sweep_corrected.csv` |
| Fig. 8 | Multi-user scaling (joint Wiener) | `revision_sims_gpu.py E7c` | `multiuser_corrected.csv` |
Quantities quoted in the text but not plotted come from the same Quantities quoted in the text but not plotted come from the same
drivers: `revision_sims.py E0` writes `theorem_check.csv` (Theorem 1 drivers: `revision_sims.py E0` writes `theorem_check.csv` (Theorem 1
@@ -59,7 +61,11 @@ Rayleigh MSE), `E4` writes `csi_error.csv` (imperfect-CSI
robustness), `E5` writes `mask_family_rev.csv` (WalshHadamard versus robustness), `E5` writes `mask_family_rev.csv` (WalshHadamard versus
Haar), `E8` writes `mismatch.csv` (affinity mismatch and Haar), `E8` writes `mismatch.csv` (affinity mismatch and
quantization), and `E9` writes `cosine_ceiling.csv` (cosine-ceiling quantization), and `E9` writes `cosine_ceiling.csv` (cosine-ceiling
corollary check). corollary check). The empirical affinity statistics quoted in the
manuscript are recomputable from `clip_realdata_beta.csv` and
`bert_vit_beta.csv` (32 paired and 32 unpaired samples per encoder
family), and the trained refinement gates behind the capacity-check
claim are stored in `refine_gates.npz`.
## Verifying the analysis ## Verifying the analysis
@@ -85,5 +91,6 @@ drawn fresh on every realization.
## Citation and license ## Citation and license
Citation details and a license will be added when the paper is Until the paper is published, cite the submitted manuscript listed
published. at the top of this file. A formal citation entry and a license
will be added upon publication.
+8 -1
View File
@@ -127,7 +127,14 @@ def train_refiners(epochs=220, steps=20, batch=48, lr=5e-4,
print(f" epoch {ep+1}: loss {float(loss.detach()):.4f} " print(f" epoch {ep+1}: loss {float(loss.detach()):.4f} "
f"(cos {float(cs.detach()):.3f})", flush=True) f"(cos {float(cs.detach()):.3f})", flush=True)
print(f" trained in {time.time()-t0:.0f}s") print(f" trained in {time.time()-t0:.0f}s")
return P_single, [p.detach() for p in params] P4 = [p.detach() for p in params]
rel = float((P_single - sum(P4) / 4).norm() / P_single.norm())
print(f" [diag] ||P1 - mean(P4)|| / ||P1|| = {rel:.3e}")
np.savez(DATA / "refine_gates.npz",
P1=P_single.cpu().numpy(),
**{f"P4_{i}": p.cpu().numpy() for i, p in enumerate(P4)})
print(" [diag] gates saved to data/refine_gates.npz")
return P_single, P4
def refine_apply(ps, z): def refine_apply(ps, z):
+51 -14
View File
@@ -36,7 +36,7 @@ LBL = {
"todma": "ToDMA-adapted", "todma": "ToDMA-adapted",
"mac": "MAC sum capacity", "mac": "MAC sum capacity",
"coop": "Full-cooperation bound", "coop": "Full-cooperation bound",
"hybrid": "EDMA + refinement stage", "hybrid": "EDMA + refinement",
} }
@@ -57,6 +57,7 @@ def save(fig, name):
# ------------------------------------------------------ fig_floor # ------------------------------------------------------ fig_floor
def fig_floor(): def fig_floor():
from matplotlib.lines import Line2D
rows = rows_of("floor_validation") rows = rows_of("floor_validation")
fig, ax = plt.subplots() fig, ax = plt.subplots()
colors = {"256": "C0", "768": "C3"} colors = {"256": "C0", "768": "C3"}
@@ -66,12 +67,10 @@ def fig_floor():
or float(r["d"]) == float(d)] or float(r["d"]) == float(d)]
snr = col(rd, "snr_db") snr = col(rd, "snr_db")
ax.plot(snr, col(rd, "mse_mc"), "o", ms=3.5, color=colors[d], ax.plot(snr, col(rd, "mse_mc"), "o", ms=3.5, color=colors[d],
mfc="none", label=rf"Monte Carlo, $d={d}$") mfc="none")
ax.plot(snr, col(rd, "mse_theory"), "-", color=colors[d], ax.plot(snr, col(rd, "mse_theory"), "-", color=colors[d])
label=rf"Theorem 1, $d={d}$")
if d == "768": if d == "768":
ax.plot(snr, col(rd, "mse_blind"), "--", color="C1", lw=1.2, ax.plot(snr, col(rd, "mse_blind"), "--", color="C1", lw=1.2)
label=LBL["blind"])
g = 1.0 - beta**2 g = 1.0 - beta**2
ax.axhline(math.sqrt(g) / 2, color="gray", lw=0.8, ls="--") ax.axhline(math.sqrt(g) / 2, color="gray", lw=0.8, ls="--")
ax.axhline(0.5, color="gray", lw=0.8, ls=":") ax.axhline(0.5, color="gray", lw=0.8, ls=":")
@@ -79,10 +78,20 @@ def fig_floor():
color="gray") color="gray")
ax.annotate(r"aware floor $\sqrt{1-\beta^2}/2$", xy=(14.0, 0.432), ax.annotate(r"aware floor $\sqrt{1-\beta^2}/2$", xy=(14.0, 0.432),
fontsize=7, color="gray") fontsize=7, color="gray")
ax.set_xlabel("Per-block SNR $\\rho$ [dB]") ax.set_xlabel("SNR $\\rho$ [dB]")
ax.set_ylabel(r"Per-user MSE $\mathbb{E}\|\hat{\mathbf{e}}_u-\mathbf{e}_u\|_2^2$") 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.set_xlim(0, 40); ax.set_ylim(0.4, 1.05)
ax.legend(loc="lower left", bbox_to_anchor=(0.02, 0.18)) # framed in-axes legend like every other result figure; composite
# handles (marker = Monte Carlo, line = Theorem 1; the convention
# is stated in the caption) keep it to three entries
handles = [
Line2D([], [], color="C0", marker="o", mfc="none", ms=3.5,
ls="-", label=rf"{LBL['edma']}, $d=256$"),
Line2D([], [], color="C3", marker="o", mfc="none", ms=3.5,
ls="-", label=rf"{LBL['edma']}, $d=768$"),
Line2D([], [], color="C1", ls="--", lw=1.2, label=LBL["blind"]),
]
ax.legend(handles=handles, loc="upper right")
save(fig, "fig_floor") save(fig, "fig_floor")
@@ -97,7 +106,7 @@ def fig_rate():
ax.plot(snr, col(rows, "oma"), "--", color="C1", label=LBL["oma"]) 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, "genie"), "-.", color="C0", label=LBL["genie"])
ax.plot(snr, col(rows, "mac"), "-", color="k", lw=1.0, label=LBL["mac"]) ax.plot(snr, col(rows, "mac"), "-", color="k", lw=1.0, label=LBL["mac"])
ax.set_xlabel("Per-block SNR $\\rho$ [dB]") ax.set_xlabel("SNR $\\rho$ [dB]")
ax.set_ylabel("Effective sum rate [bps/Hz]") ax.set_ylabel("Effective sum rate [bps/Hz]")
ax.set_xlim(0, 40); ax.set_ylim(0, 3.2) ax.set_xlim(0, 40); ax.set_ylim(0, 3.2)
ax.legend(loc="upper left") ax.legend(loc="upper left")
@@ -121,10 +130,10 @@ def fig_beta_sweep():
ax.plot([], [], ls="--", color="gray", label=LBL["oma"]) ax.plot([], [], ls="--", color="gray", label=LBL["oma"])
ax.plot([], [], ls="-.", color="gray", label=LBL["genie"]) ax.plot([], [], ls="-.", color="gray", label=LBL["genie"])
for b0 in (0.030, 0.311): for b0 in (0.030, 0.311):
ax.axvline(b0, color="gray", ls=":", lw=0.9) ax.axvline(b0, ymax=0.54, color="gray", ls=":", lw=0.9)
ax.set_xlabel(r"Pairwise affinity $\beta$") ax.set_xlabel(r"Pairwise affinity $\beta$")
ax.set_ylabel("Effective sum rate [bps/Hz]") ax.set_ylabel("Effective sum rate [bps/Hz]")
ax.set_xlim(0, 1); ax.set_ylim(0, 1.0) ax.set_xlim(0, 1); ax.set_ylim(0, 1.05)
ax.legend(loc="upper left") ax.legend(loc="upper left")
save(fig, "fig_beta_sweep_corrected") save(fig, "fig_beta_sweep_corrected")
@@ -140,7 +149,7 @@ def fig_sic():
ax.plot(snr, col(rows, "oma"), "v--", color="C1", label=LBL["oma"]) ax.plot(snr, col(rows, "oma"), "v--", color="C1", label=LBL["oma"])
ax.plot(snr, col(rows, "genie"), "-", color="gray", lw=1.0, ax.plot(snr, col(rows, "genie"), "-", color="gray", lw=1.0,
label=LBL["genie"]) label=LBL["genie"])
ax.set_xlabel("Per-block SNR $\\rho$ [dB]") ax.set_xlabel("SNR $\\rho$ [dB]")
ax.set_ylabel("Mean cosine similarity") ax.set_ylabel("Mean cosine similarity")
ax.set_xlim(snr[0], snr[-1]); ax.set_ylim(0, 0.7) ax.set_xlim(snr[0], snr[-1]); ax.set_ylim(0, 0.7)
ax.legend(loc="upper left") ax.legend(loc="upper left")
@@ -162,18 +171,46 @@ def fig_multiuser():
mk = [i for i, s in enumerate(snr) if s % 5 == 0] 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], 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") "o", color=colors[U], ms=4, mfc="none")
ax.set_xlabel("Per-block SNR $\\rho$ [dB]") ax.set_xlabel("SNR $\\rho$ [dB]")
ax.set_ylabel("Effective sum rate [bps/Hz]") ax.set_ylabel("Effective sum rate [bps/Hz]")
ax.set_xlim(0, 30) ax.set_xlim(0, 30)
ax.set_ylim(bottom=0)
ax.legend(loc="upper left") ax.legend(loc="upper left")
save(fig, "fig_multiuser_corrected") save(fig, "fig_multiuser_corrected")
# ------------------------------------------------ fig_retrieval
def fig_retrieval():
rows = rows_of("retrieval_real")
snr = col(rows, "snr_db")
fig, ax = plt.subplots()
ax.plot(snr, col(rows, "edma"), "o-", color="C3", label=LBL["edma"])
ax.plot(snr, col(rows, "hybrid"), "^-", color="C2",
label=LBL["hybrid"])
ax.plot(snr, col(rows, "todma"), "d-.", color="C4",
label=LBL["todma"])
ax.plot(snr, col(rows, "oma"), "v:", color="C1", label=LBL["oma"])
# genie curve overlaps EDMA here; named in the caption, not the legend
ax.plot(snr, col(rows, "genie"), "-", color="gray", lw=1.0,
label="_nolegend_")
ax.axhline(1.0 / 16, xmax=0.48, color="gray", ls=":", lw=0.8)
ax.annotate("chance $1/16$", xy=(1.2, 1.0 / 16 + 0.015), fontsize=7,
color="gray")
ax.set_xlabel("SNR $\\rho$ [dB]")
ax.set_ylabel("Top-1 retrieval accuracy")
ax.set_xlim(snr[0], snr[-1]); ax.set_ylim(0, 1.0)
ax.legend(loc="lower right", borderaxespad=0.3,
handlelength=1.4, handletextpad=0.5,
labelspacing=0.35)
save(fig, "fig_retrieval")
if __name__ == "__main__": if __name__ == "__main__":
import sys import sys
todo = set(sys.argv[1:]) todo = set(sys.argv[1:])
ALL = {"floor": fig_floor, "rate": fig_rate, "beta": fig_beta_sweep, ALL = {"floor": fig_floor, "rate": fig_rate, "beta": fig_beta_sweep,
"sic": fig_sic, "multi": fig_multiuser} "sic": fig_sic, "multi": fig_multiuser,
"retr": fig_retrieval}
for name, fn in ALL.items(): for name, fn in ALL.items():
if not todo or name in todo: if not todo or name in todo:
fn() fn()
+8 -7
View File
@@ -7,6 +7,7 @@ from pathlib import Path
import matplotlib import matplotlib
matplotlib.use("Agg") matplotlib.use("Agg")
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from replot_all import LBL
ROOT = Path(__file__).resolve().parents[1] ROOT = Path(__file__).resolve().parents[1]
plt.rcParams.update({ plt.rcParams.update({
@@ -25,17 +26,17 @@ snr = [float(r["snr_db"]) for r in rows]
col = lambda k: [float(r[k]) for r in rows] col = lambda k: [float(r[k]) for r in rows]
fig, ax = plt.subplots() fig, ax = plt.subplots()
ax.plot(snr, col("edma"), "o-", color="C3", label="EDMA") ax.plot(snr, col("edma"), "o-", color="C3", label=LBL["edma"])
ax.plot(snr, col("edma_ref"), "^-", color="C2", ax.plot(snr, col("edma_ref"), "^-", color="C2",
label="EDMA + refinement stage") label=LBL["hybrid"])
ax.plot(snr, col("todma"), "d-.", color="C4", label="ToDMA-adapted") ax.plot(snr, col("todma"), "d-.", color="C4", label=LBL["todma"])
ax.plot(snr, col("oma"), "v:", color="C1", label="OMA") ax.plot(snr, col("oma"), "v:", color="C1", label=LBL["oma"])
ax.plot(snr, col("genie"), "-", color="gray", lw=1.0, ax.plot(snr, col("genie"), "-", color="gray", lw=1.0,
label="Genie-aided SIC bound") label=LBL["genie"])
ax.set_xlabel("Per-block SNR $\\rho$ [dB]") ax.set_xlabel("SNR $\\rho$ [dB]")
ax.set_ylabel("Mean cosine similarity") ax.set_ylabel("Mean cosine similarity")
ax.set_xlim(snr[0], snr[-1]) ax.set_xlim(snr[0], snr[-1])
ax.set_ylim(0, 0.85) ax.set_ylim(0, 0.75)
ax.legend(loc="upper left") ax.legend(loc="upper left")
fig.subplots_adjust(**AXES_RECT) fig.subplots_adjust(**AXES_RECT)
fig.savefig(ROOT / "fig" / "fig_bertvit_merged.pdf") fig.savefig(ROOT / "fig" / "fig_bertvit_merged.pdf")
+147
View File
@@ -0,0 +1,147 @@
"""
Task-level validation: nearest-neighbor retrieval with recovered
embeddings on the real BERT/ViT pairs.
===================================================================
For every recovered embedding, rank the 16 clean database embeddings
of the SAME modality by absolute cosine and score top-1 retrieval of
the transmitted item (chance level 1/16). This measures whether the
recovery preserves semantic identity, the operational question behind
the cosine metric of the manuscript.
Schemes: EDMA (aware Wiener), hybrid (EDMA + stored refinement gate
from data/refine_gates.npz), OMA (equivalent-bandwidth model),
genie-aided SIC bound, ToDMA-adapted (first 40 fading draws).
Same channel, energy, and seed conventions as fig_real_merged.py.
Writes data/retrieval_real.csv. Run under WSL (torch, CUDA).
"""
from __future__ import annotations
import csv
import math
import time
import numpy as np
import torch
from fig_real_merged import (load_pairs, haar_t, aware_batch, todma_prepare,
omp_code, todma_run, SNRS, NFADE, NFADE_TOD,
D, DATA, DEV, SEED)
torch.manual_seed(SEED)
rng = np.random.default_rng(SEED)
def refine_apply_single(P1, z):
"""z: (b, D) real torch tensor; P1: (D, D) gate."""
return D * torch.softmax((z @ P1.T) / math.sqrt(D), dim=1) * z
def top1(rec, db, idx):
"""rec: (b, D) cfloat; db: (n, D) float; returns (b,) 0/1 hits."""
sims = (rec @ db.T.to(rec.dtype).conj()).abs() # (b, n)
sims = sims / (rec.norm(dim=1, keepdim=True)
* db.norm(dim=1).unsqueeze(0))
return (sims.argmax(dim=1) == idx).float().cpu().numpy()
def main():
A, B, betas = load_pairs()
npairs = len(A)
gates = np.load(DATA / "refine_gates.npz")
P1 = torch.tensor(gates["P1"], dtype=torch.float32, device=DEV)
tod = todma_prepare()
codes = [(omp_code(tod[0], A[i], tod[3]),
omp_code(tod[0], B[i], tod[3])) for i in range(npairs)]
At = torch.tensor(A, dtype=torch.float32, device=DEV)
Bt = torch.tensor(B, dtype=torch.float32, device=DEV)
gen = torch.Generator(device=DEV).manual_seed(SEED)
nb = len(SNRS)
sigs = torch.tensor(10 ** (-SNRS / 20.0), dtype=torch.float32,
device=DEV)
keys = ("edma", "hybrid", "oma", "genie", "todma")
acc = {k: np.zeros(nb) for k in keys}
cnt = {k: np.zeros(nb) for k in keys}
t0 = time.time()
for i in range(npairs):
bi = float(betas[i])
e1, e2 = At[i], Bt[i]
c1c, c2c = codes[i]
for f in range(NFADE):
M = haar_t(2, gen)
M1, M2 = M[0], M[1]
Q = M1.T @ M2
h = (torch.randn(2, generator=gen, device=DEV)
+ 1j * torch.randn(2, generator=gen, device=DEV)) \
/ math.sqrt(2)
n = (torch.randn(D, generator=gen, device=DEV)
+ 1j * torch.randn(D, generator=gen, device=DEV)) \
/ math.sqrt(2)
n2 = (torch.randn(D, generator=gen, device=DEV)
+ 1j * torch.randn(D, generator=gen, device=DEV)) \
/ math.sqrt(2)
r0 = h[0] * (M1 @ e1).to(torch.cfloat) \
+ h[1] * (M2 @ e2).to(torch.cfloat)
r = r0.unsqueeze(0) + sigs.view(-1, 1) * n.unsqueeze(0)
t1 = (M1.T.to(torch.cfloat) @ r.unsqueeze(-1)).squeeze(-1) / h[0]
t2 = (M2.T.to(torch.cfloat) @ r.unsqueeze(-1)).squeeze(-1) / h[1]
c1 = (h[1] / h[0]).item()
c2 = (h[0] / h[1]).item()
v1 = sigs**2 / h[0].abs()**2
v2 = sigs**2 / h[1].abs()**2
g1 = aware_batch(t1, Q, bi, c1, v1)
g2 = aware_batch(t2, Q.T, bi, c2, v2)
acc["edma"] += 0.5 * (top1(g1, At, i) + top1(g2, Bt, i))
hy1 = refine_apply_single(P1, g1.real.float()).to(torch.cfloat)
hy2 = refine_apply_single(P1, g2.real.float()).to(torch.cfloat)
acc["hybrid"] += 0.5 * (top1(hy1, At, i) + top1(hy2, Bt, i))
o1 = e1.to(torch.cfloat).unsqueeze(0) \
+ math.sqrt(2) * sigs.view(-1, 1) * n.unsqueeze(0) / h[0]
o2 = e2.to(torch.cfloat).unsqueeze(0) \
+ math.sqrt(2) * sigs.view(-1, 1) * n2.unsqueeze(0) / h[1]
acc["oma"] += 0.5 * (top1(o1, At, i) + top1(o2, Bt, i))
ge1 = (M1.T.to(torch.cfloat)
@ (r - h[1] * (M2 @ e2).to(torch.cfloat)).unsqueeze(-1)
).squeeze(-1) / h[0]
ge2 = (M2.T.to(torch.cfloat)
@ (r - h[0] * (M1 @ e1).to(torch.cfloat)).unsqueeze(-1)
).squeeze(-1) / h[1]
acc["genie"] += 0.5 * (top1(ge1, At, i) + top1(ge2, Bt, i))
for kk in ("edma", "hybrid", "oma", "genie"):
cnt[kk] += 1
if f < NFADE_TOD:
hnp = (complex(h[0].item()), complex(h[1].item()))
nslots = [(rng.standard_normal(tod[4])
+ 1j * rng.standard_normal(tod[4]))
/ math.sqrt(2) for _ in range(tod[3])]
for k, s in enumerate(SNRS):
sig = 10 ** (-s / 20.0)
recs = todma_run(tod, (c1c, c2c), hnp, sig, nslots)
hit = 0.0
for j, (rec, db, ii) in enumerate(
((recs[0], A, i), (recs[1], B, i))):
if rec is None:
continue # failed detection: no hit
sims = np.abs(db @ rec) / (
np.linalg.norm(db, axis=1)
* np.linalg.norm(rec))
hit += 0.5 * float(int(np.argmax(sims)) == ii)
acc["todma"][k] += hit
cnt["todma"][k] += 1
print(f" pair {i+1}/{npairs} done ({time.time()-t0:.0f}s)",
flush=True)
for k in keys:
acc[k] /= np.maximum(cnt[k], 1)
with open(DATA / "retrieval_real.csv", "w", newline="") as f:
w = csv.writer(f)
w.writerow(["snr_db"] + list(keys))
for k, s in enumerate(SNRS):
w.writerow([s] + [acc[key][k] for key in keys])
print(f"[OK] wrote {DATA/'retrieval_real.csv'}")
for k, s in enumerate(SNRS):
print(f" {s:4.1f} dB EDMA {acc['edma'][k]:.3f} "
f"hybrid {acc['hybrid'][k]:.3f} ToDMA {acc['todma'][k]:.3f} "
f" OMA {acc['oma'][k]:.3f} genie {acc['genie'][k]:.3f}")
if __name__ == "__main__":
main()
+2 -2
View File
@@ -246,7 +246,7 @@ def E1_floor(beta=0.311, dims=(256, 768), snr_db=np.arange(0, 41, 2.5), ntr=120)
ax.text(22.0, 0.40, r"aware floor $\sqrt{1-\beta^2}/2$", ax.text(22.0, 0.40, r"aware floor $\sqrt{1-\beta^2}/2$",
fontsize=7, color="gray") fontsize=7, color="gray")
ax.set_yscale("linear") ax.set_yscale("linear")
ax.set_xlabel("Per-block SNR $\\rho$ [dB]") ax.set_xlabel("SNR $\\rho$ [dB]")
ax.set_ylabel(r"Per-user MSE $\mathbb{E}\|\hat{\mathbf{e}}_u-\mathbf{e}_u\|_2^2$") 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.set_xlim(0, 40); ax.set_ylim(0.4, 1.05)
ax.legend(loc="upper right", ncol=1) ax.legend(loc="upper right", ncol=1)
@@ -284,7 +284,7 @@ def E7_rates(beta=0.311, d=512):
ax.plot(snr_db, To, "--", color="C1", label=LBL["oma"]) ax.plot(snr_db, To, "--", color="C1", label=LBL["oma"])
ax.plot(snr_db, Tg, "-.", color="C0", label=LBL["genie"]) ax.plot(snr_db, Tg, "-.", color="C0", label=LBL["genie"])
ax.plot(snr_db, Cm, "-", color="k", lw=1.0, label=LBL["mac"]) ax.plot(snr_db, Cm, "-", color="k", lw=1.0, label=LBL["mac"])
ax.set_xlabel("Per-block SNR $\\rho$ [dB]") ax.set_xlabel("SNR $\\rho$ [dB]")
ax.set_ylabel("Effective sum rate [bps/Hz]") ax.set_ylabel("Effective sum rate [bps/Hz]")
ax.set_xlim(0, 40); ax.set_ylim(0, 3.2) ax.set_xlim(0, 40); ax.set_ylim(0, 3.2)
ax.legend(loc="upper left") ax.legend(loc="upper left")
+66 -1
View File
@@ -405,11 +405,76 @@ def E9_ceiling(d=512, snr=60.0, ntr=200):
'blind_mc', 'blind_pred'], rows) 'blind_mc', 'blind_pred'], rows)
# ------------------------------------------------------------------
def E10_whpad(beta=0.311, d=768, dpad=1024, ntr=200):
"""Zero-padded WH at d=768 (padded to 1024) vs dense Haar at 768.
The embedding (768) is zero-padded to 1024, masked by H_1024 D_u,
and the exact per-coordinate Wiener uses the true prior (signal
variance 1/768 on the active support, zero on the padding, so the
padded coordinates are discarded). Reference: Haar masks at the
native d=768 with the standard aware demultiplexer. Same per-block
energy E_b = 1 and the same noise PSD; the padded block occupies
dpad channel uses, a bandwidth cost of dpad/d."""
print(chr(10) + '=== E10: zero-padded WH (768->1024) vs native Haar 768 ===')
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] < dpad:
H = np.block([[H, H], [H, -H]])
Ht = torch.tensor(H / math.sqrt(dpad), dtype=torch.float32, device=DEV)
g = 1.0 - beta**2
res = {'haar': np.zeros(nb), 'whpad': np.zeros(nb)}
for _ in range(ntr):
e1, e2 = embed_pair(d, beta)
# ---- native Haar at 768 ----
M1, M2 = haar_g(d), haar_g(d)
Q = M1.T @ M2
n = cnoise_g(d)
r = (M1 @ e1 + M2 @ e2).to(torch.complex64).unsqueeze(0) \
+ sigs.view(-1, 1) * n.unsqueeze(0)
t1 = (M1.T.to(torch.complex64) @ r.unsqueeze(-1)).squeeze(-1)
g1 = aware_g(t1, Q, beta, 1.0, sigs**2)
res['haar'] += abscos(g1, e1)
# ---- zero-padded WH at 1024 ----
z = torch.zeros(dpad - d, device=DEV)
e1p = torch.cat([e1, z]); e2p = torch.cat([e2, z])
D1 = torch.tensor(np.sign(rng.standard_normal(dpad)),
dtype=torch.float32, device=DEV)
D2 = torch.tensor(np.sign(rng.standard_normal(dpad)),
dtype=torch.float32, device=DEV)
W1, W2 = Ht * D1.unsqueeze(0), Ht * D2.unsqueeze(0)
npad = cnoise_g(dpad)
rp = (W1 @ e1p + W2 @ e2p).to(torch.complex64).unsqueeze(0) \
+ sigs.view(-1, 1) * npad.unsqueeze(0)
tw = (W1.T.to(torch.complex64) @ rp.unsqueeze(-1)).squeeze(-1)
q = (D1 * D2)[:d] # active coordinates only
a = 1.0 + beta * q # (d,)
s_var = 1.0 / d # true signal variance
v = g / d + (sigs**2).view(-1, 1) # interference + noise
gains = (s_var * a.unsqueeze(0)) / (a.unsqueeze(0)**2 * s_var + v)
w1 = gains.to(torch.complex64) * tw[:, :d]
res['whpad'] += abscos(w1, e1)
for k in res:
res[k] /= ntr
rows = [[s, res['haar'][i], res['whpad'][i]]
for i, s in enumerate(snr_db)]
write_csv('wh_padding', ['snr_db', 'haar768', 'whpad1024'], rows)
dev = res['haar'] - res['whpad']
print(f' cosine delta (haar - whpad): max {dev.max():.4f}, '
f'at 20 dB {dev[list(snr_db).index(20)]:.4f}, '
f'at 40 dB {dev[-1]:.4f}')
print(f' bandwidth cost: {dpad}/{d} = {dpad/d:.3f}x uses '
f'(per-use rate factor {d/dpad:.3f})')
if __name__ == "__main__": if __name__ == "__main__":
todo = set(sys.argv[1:]) todo = set(sys.argv[1:])
ALL = {"E2": E2_sic, "E3": E3_unconditional, "E4": E4_csi, ALL = {"E2": E2_sic, "E3": E3_unconditional, "E4": E4_csi,
"E5": E5_maskfam, "E7c": E7_multiuser, "E8": E8_mismatch, "E5": E5_maskfam, "E7c": E7_multiuser, "E8": E8_mismatch,
"E9": E9_ceiling} "E9": E9_ceiling, "E10": E10_whpad}
for name, fn in ALL.items(): for name, fn in ALL.items():
if not todo or name in todo: if not todo or name in todo:
fn() fn()
+65
View File
@@ -0,0 +1,65 @@
scheme,beta
paired,0.027237803175426414
paired,0.012896661680831107
paired,0.010416352482946263
paired,0.048123426570364544
paired,0.02378884879050521
paired,0.08129956173740625
paired,0.019008848452584104
paired,0.008772706088568172
paired,0.005386448106588374
paired,0.04672583614390642
paired,0.02654219206213419
paired,0.008971354550608865
paired,0.006527803719580855
paired,0.0003781749165654546
paired,0.040345337710264106
paired,0.06631996053522116
paired,0.023303015182582845
paired,0.0036342242085544724
paired,0.01080984433510444
paired,0.05122711771786337
paired,0.02196780882017706
paired,0.027430766747928603
paired,0.05481789805580116
paired,0.09104140849083153
paired,0.01815058092411296
paired,0.03022733891851148
paired,0.007488021664737729
paired,0.0023511041567845174
paired,0.09862665895072077
paired,0.048736810747693156
paired,0.011754320764163387
paired,0.036121899180464434
unpaired,0.019374828604252024
unpaired,0.032440294233851884
unpaired,0.02378860498148811
unpaired,0.008587382855481418
unpaired,0.05651731568397114
unpaired,0.02403075688014663
unpaired,0.03458268967264902
unpaired,0.03784547767683299
unpaired,0.0027191357506518334
unpaired,0.06647789537369261
unpaired,0.028248259683892177
unpaired,0.02975719499694031
unpaired,0.002693260517757482
unpaired,0.00956545888624823
unpaired,0.018776037962855974
unpaired,0.01723862993724272
unpaired,0.003321530987236865
unpaired,0.026794936423899428
unpaired,0.005555610068283409
unpaired,0.02096161483617256
unpaired,0.029145082852780703
unpaired,0.008083174012553023
unpaired,0.009863492000612803
unpaired,0.016990287948247137
unpaired,0.009933587496519476
unpaired,0.06247402736383933
unpaired,0.020214432381559597
unpaired,0.06931381025435437
unpaired,0.02564802276256673
unpaired,0.04367255778838598
unpaired,0.01957917212315197
unpaired,0.06779897449994185
1 scheme beta
2 paired 0.027237803175426414
3 paired 0.012896661680831107
4 paired 0.010416352482946263
5 paired 0.048123426570364544
6 paired 0.02378884879050521
7 paired 0.08129956173740625
8 paired 0.019008848452584104
9 paired 0.008772706088568172
10 paired 0.005386448106588374
11 paired 0.04672583614390642
12 paired 0.02654219206213419
13 paired 0.008971354550608865
14 paired 0.006527803719580855
15 paired 0.0003781749165654546
16 paired 0.040345337710264106
17 paired 0.06631996053522116
18 paired 0.023303015182582845
19 paired 0.0036342242085544724
20 paired 0.01080984433510444
21 paired 0.05122711771786337
22 paired 0.02196780882017706
23 paired 0.027430766747928603
24 paired 0.05481789805580116
25 paired 0.09104140849083153
26 paired 0.01815058092411296
27 paired 0.03022733891851148
28 paired 0.007488021664737729
29 paired 0.0023511041567845174
30 paired 0.09862665895072077
31 paired 0.048736810747693156
32 paired 0.011754320764163387
33 paired 0.036121899180464434
34 unpaired 0.019374828604252024
35 unpaired 0.032440294233851884
36 unpaired 0.02378860498148811
37 unpaired 0.008587382855481418
38 unpaired 0.05651731568397114
39 unpaired 0.02403075688014663
40 unpaired 0.03458268967264902
41 unpaired 0.03784547767683299
42 unpaired 0.0027191357506518334
43 unpaired 0.06647789537369261
44 unpaired 0.028248259683892177
45 unpaired 0.02975719499694031
46 unpaired 0.002693260517757482
47 unpaired 0.00956545888624823
48 unpaired 0.018776037962855974
49 unpaired 0.01723862993724272
50 unpaired 0.003321530987236865
51 unpaired 0.026794936423899428
52 unpaired 0.005555610068283409
53 unpaired 0.02096161483617256
54 unpaired 0.029145082852780703
55 unpaired 0.008083174012553023
56 unpaired 0.009863492000612803
57 unpaired 0.016990287948247137
58 unpaired 0.009933587496519476
59 unpaired 0.06247402736383933
60 unpaired 0.020214432381559597
61 unpaired 0.06931381025435437
62 unpaired 0.02564802276256673
63 unpaired 0.04367255778838598
64 unpaired 0.01957917212315197
65 unpaired 0.06779897449994185
+65
View File
@@ -0,0 +1,65 @@
scheme,beta
paired,0.2875853478908539
paired,0.33386528491973877
paired,0.3495585322380066
paired,0.2988513112068176
paired,0.3365963399410248
paired,0.28957056999206543
paired,0.3398767411708832
paired,0.2994540333747864
paired,0.3134581446647644
paired,0.2800610363483429
paired,0.33044949173927307
paired,0.3525742292404175
paired,0.28622955083847046
paired,0.3221920132637024
paired,0.34046676754951477
paired,0.3445603847503662
paired,0.3361469507217407
paired,0.315978467464447
paired,0.2871871590614319
paired,0.20464202761650085
paired,0.33651721477508545
paired,0.28726524114608765
paired,0.26744377613067627
paired,0.2686237692832947
paired,0.2603684663772583
paired,0.32788804173469543
paired,0.28169411420822144
paired,0.3227519690990448
paired,0.3319033086299896
paired,0.3185873031616211
paired,0.34493112564086914
paired,0.3604525923728943
unpaired,0.13933990895748138
unpaired,0.10618321597576141
unpaired,0.16058675944805145
unpaired,0.17256049811840057
unpaired,0.18050867319107056
unpaired,0.17393678426742554
unpaired,0.17463842034339905
unpaired,0.12813161313533783
unpaired,0.14834891259670258
unpaired,0.16522526741027832
unpaired,0.15986862778663635
unpaired,0.19971923530101776
unpaired,0.21379850804805756
unpaired,0.16859294474124908
unpaired,0.1630430519580841
unpaired,0.1932932436466217
unpaired,0.1589049994945526
unpaired,0.15467114746570587
unpaired,0.21967162191867828
unpaired,0.1271267682313919
unpaired,0.1379617303609848
unpaired,0.11341479420661926
unpaired,0.1405247300863266
unpaired,0.1693478673696518
unpaired,0.2000925987958908
unpaired,0.1594662368297577
unpaired,0.1274571418762207
unpaired,0.18167132139205933
unpaired,0.14616280794143677
unpaired,0.1484544575214386
unpaired,0.14663159847259521
unpaired,0.1430159956216812
1 scheme beta
2 paired 0.2875853478908539
3 paired 0.33386528491973877
4 paired 0.3495585322380066
5 paired 0.2988513112068176
6 paired 0.3365963399410248
7 paired 0.28957056999206543
8 paired 0.3398767411708832
9 paired 0.2994540333747864
10 paired 0.3134581446647644
11 paired 0.2800610363483429
12 paired 0.33044949173927307
13 paired 0.3525742292404175
14 paired 0.28622955083847046
15 paired 0.3221920132637024
16 paired 0.34046676754951477
17 paired 0.3445603847503662
18 paired 0.3361469507217407
19 paired 0.315978467464447
20 paired 0.2871871590614319
21 paired 0.20464202761650085
22 paired 0.33651721477508545
23 paired 0.28726524114608765
24 paired 0.26744377613067627
25 paired 0.2686237692832947
26 paired 0.2603684663772583
27 paired 0.32788804173469543
28 paired 0.28169411420822144
29 paired 0.3227519690990448
30 paired 0.3319033086299896
31 paired 0.3185873031616211
32 paired 0.34493112564086914
33 paired 0.3604525923728943
34 unpaired 0.13933990895748138
35 unpaired 0.10618321597576141
36 unpaired 0.16058675944805145
37 unpaired 0.17256049811840057
38 unpaired 0.18050867319107056
39 unpaired 0.17393678426742554
40 unpaired 0.17463842034339905
41 unpaired 0.12813161313533783
42 unpaired 0.14834891259670258
43 unpaired 0.16522526741027832
44 unpaired 0.15986862778663635
45 unpaired 0.19971923530101776
46 unpaired 0.21379850804805756
47 unpaired 0.16859294474124908
48 unpaired 0.1630430519580841
49 unpaired 0.1932932436466217
50 unpaired 0.1589049994945526
51 unpaired 0.15467114746570587
52 unpaired 0.21967162191867828
53 unpaired 0.1271267682313919
54 unpaired 0.1379617303609848
55 unpaired 0.11341479420661926
56 unpaired 0.1405247300863266
57 unpaired 0.1693478673696518
58 unpaired 0.2000925987958908
59 unpaired 0.1594662368297577
60 unpaired 0.1274571418762207
61 unpaired 0.18167132139205933
62 unpaired 0.14616280794143677
63 unpaired 0.1484544575214386
64 unpaired 0.14663159847259521
65 unpaired 0.1430159956216812
Binary file not shown.
+14
View File
@@ -0,0 +1,14 @@
snr_db,edma,hybrid,oma,genie,todma
0.0,0.1915625,0.2253125,0.1278125,0.190625,0.028125
2.5,0.278125,0.325,0.1884375,0.2840625,0.03671875
5.0,0.4065625,0.45375,0.2784375,0.4103125,0.05390625
7.5,0.5365625,0.5765625,0.3965625,0.54,0.0765625
10.0,0.6625,0.6940625,0.52375,0.6675,0.1390625
12.5,0.7646875,0.781875,0.6515625,0.76375,0.23359375
15.0,0.8296875,0.84875,0.7515625,0.833125,0.371875
17.5,0.8828125,0.8934375,0.828125,0.89,0.515625
20.0,0.9178125,0.926875,0.881875,0.925625,0.65859375
22.5,0.94625,0.9503125,0.9175,0.9565625,0.7703125
25.0,0.96125,0.9625,0.94625,0.9734375,0.84609375
27.5,0.970625,0.971875,0.9690625,0.983125,0.8921875
30.0,0.9796875,0.98,0.9809375,0.99125,0.91640625
1 snr_db edma hybrid oma genie todma
2 0.0 0.1915625 0.2253125 0.1278125 0.190625 0.028125
3 2.5 0.278125 0.325 0.1884375 0.2840625 0.03671875
4 5.0 0.4065625 0.45375 0.2784375 0.4103125 0.05390625
5 7.5 0.5365625 0.5765625 0.3965625 0.54 0.0765625
6 10.0 0.6625 0.6940625 0.52375 0.6675 0.1390625
7 12.5 0.7646875 0.781875 0.6515625 0.76375 0.23359375
8 15.0 0.8296875 0.84875 0.7515625 0.833125 0.371875
9 17.5 0.8828125 0.8934375 0.828125 0.89 0.515625
10 20.0 0.9178125 0.926875 0.881875 0.925625 0.65859375
11 22.5 0.94625 0.9503125 0.9175 0.9565625 0.7703125
12 25.0 0.96125 0.9625 0.94625 0.9734375 0.84609375
13 27.5 0.970625 0.971875 0.9690625 0.983125 0.8921875
14 30.0 0.9796875 0.98 0.9809375 0.99125 0.91640625
+10
View File
@@ -0,0 +1,10 @@
snr_db,haar768,whpad1024
0,0.04595265286625363,0.047847944343229754
5,0.07112330510281026,0.07224809597013518
10,0.1199369035474956,0.12063940849155187
15,0.20449634090065957,0.20457616232335568
20,0.33400419175624846,0.3319106823205948
25,0.48966493368148806,0.48235686495900154
30,0.6161512869596482,0.6032128128409385
35,0.6835479807853698,0.6678868445754051
40,0.7102783480286599,0.6937726792693139
1 snr_db haar768 whpad1024
2 0 0.04595265286625363 0.047847944343229754
3 5 0.07112330510281026 0.07224809597013518
4 10 0.1199369035474956 0.12063940849155187
5 15 0.20449634090065957 0.20457616232335568
6 20 0.33400419175624846 0.3319106823205948
7 25 0.48966493368148806 0.48235686495900154
8 30 0.6161512869596482 0.6032128128409385
9 35 0.6835479807853698 0.6678868445754051
10 40 0.7102783480286599 0.6937726792693139
Binary file not shown.
+3 -3
View File
@@ -6,7 +6,7 @@
\usetikzlibrary{arrows.meta,positioning,fit,calc} \usetikzlibrary{arrows.meta,positioning,fit,calc}
\begin{document} \begin{document}
\begin{tikzpicture}[ \begin{tikzpicture}[
font=\footnotesize, font=\small,
node distance=3.2mm and 4.5mm, node distance=3.2mm and 4.5mm,
blk/.style={draw, semithick, minimum height=5.5mm, minimum width=9mm, blk/.style={draw, semithick, minimum height=5.5mm, minimum width=9mm,
inner sep=1.5pt, align=center}, inner sep=1.5pt, align=center},
@@ -58,8 +58,8 @@
\draw[arr] (sig) -- node[above, lbl] {$\mb{r}$} (mf); \draw[arr] (sig) -- node[above, lbl] {$\mb{r}$} (mf);
\draw[arr] (mf) -- node[above, lbl] {$\mb{t}_u$} (wnr); \draw[arr] (mf) -- node[above, lbl] {$\mb{t}_u$} (wnr);
\draw[arr] (wnr) -- (out); \draw[arr] (wnr) -- (out);
\fill (tap1) circle (0.5pt); \fill (tap1) circle (1.1pt);
\fill (tapU) circle (0.5pt); \fill (tapU) circle (1.1pt);
\draw[dsh] (tap1) -- ($(tap1 |- bm.north)$); \draw[dsh] (tap1) -- ($(tap1 |- bm.north)$);
\draw[dsh] (bm.east) -| (wnr.south); \draw[dsh] (bm.east) -| (wnr.south);
\end{tikzpicture} \end{tikzpicture}
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.