EDMA reproducibility package: simulation code, result data, and manuscript figures
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
out/
|
||||
.ipynb_checkpoints/
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
@@ -0,0 +1,77 @@
|
||||
# EDMA — Affinity-Aware Embedding Division Multiple Access
|
||||
|
||||
Reproducibility package for
|
||||
|
||||
> K.-H. Lee, H.-H. Choi, and J.-R. Lee, "Affinity-Aware Embedding
|
||||
> Division Multiple Access for Multi-User Semantic Communications,"
|
||||
> submitted to *IEEE Transactions on Vehicular Technology*, 2026.
|
||||
|
||||
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.
|
||||
|
||||
## Layout
|
||||
|
||||
| Folder | Contents |
|
||||
|---|---|
|
||||
| `code/` | Simulation and plotting scripts (Python, CPU only) |
|
||||
| `data/` | Raw results written by the scripts, one CSV per experiment |
|
||||
| `fig/` | Figure PDFs included in the manuscript |
|
||||
|
||||
## 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.
|
||||
|
||||
## Reproducing the figures
|
||||
|
||||
Run the scripts from inside `code/`.
|
||||
|
||||
| Figure | Content | Script | Data |
|
||||
|---|---|---|---|
|
||||
| Fig. 2 | Per-user MSE and self-interference 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.
|
||||
|
||||
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.
|
||||
|
||||
## 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`.
|
||||
|
||||
## 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.
|
||||
|
||||
## Citation and license
|
||||
|
||||
Citation details and a license will be added when the paper is
|
||||
published.
|
||||
@@ -0,0 +1,333 @@
|
||||
"""
|
||||
Merged real-data comparison figure (replaces separate Figs 4 and 5).
|
||||
===================================================================
|
||||
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:
|
||||
|
||||
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).
|
||||
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
|
||||
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.
|
||||
Seed fixed.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import csv
|
||||
import math
|
||||
import pickle
|
||||
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)
|
||||
|
||||
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))
|
||||
|
||||
|
||||
def unit(v):
|
||||
return v / np.linalg.norm(v)
|
||||
|
||||
|
||||
def cosine(a, b):
|
||||
return float(abs(np.vdot(a, b)) / (np.linalg.norm(a) * np.linalg.norm(b)))
|
||||
|
||||
|
||||
def load_pairs():
|
||||
a, b = pickle.load(open(DATA / "bert_vit_cached.pkl", "rb"))
|
||||
a = np.stack([unit(x - x.mean()) for x in a])
|
||||
b = np.stack([unit(x - x.mean()) for x in b])
|
||||
betas = np.abs((a * b).sum(1))
|
||||
print(f"[pairs] {len(a)} cached BERT/ViT pairs, d={a.shape[1]}, "
|
||||
f"beta mean {betas.mean():.4f} std {betas.std():.4f}")
|
||||
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 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 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
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# ToDMA-adapted on real embeddings (complex channel)
|
||||
# ------------------------------------------------------------------
|
||||
def todma_prepare(V=1024, T=16):
|
||||
L = D // T
|
||||
Dict = rng.standard_normal((V, D))
|
||||
Dict /= np.linalg.norm(Dict, axis=1, keepdims=True)
|
||||
Sig = rng.standard_normal((V, L))
|
||||
Sig /= np.linalg.norm(Sig, axis=1, keepdims=True)
|
||||
amp = math.sqrt(1.0 / T) # E_b = 1 per user per block
|
||||
return Dict, Sig, amp, T, L
|
||||
|
||||
|
||||
def omp_code(Dict, e, T):
|
||||
resid = e.copy(); idx = []
|
||||
for _ in range(T):
|
||||
corr = np.abs(Dict @ resid)
|
||||
if idx:
|
||||
corr[idx] = -1
|
||||
k = int(corr.argmax()); idx.append(k)
|
||||
A = Dict[idx].T
|
||||
coef, *_ = np.linalg.lstsq(A, e, rcond=None)
|
||||
resid = e - A @ coef
|
||||
return idx, coef
|
||||
|
||||
|
||||
def todma_run(tod, codes, h, sig, noise_slots):
|
||||
Dict, Sig, amp, T, L = tod
|
||||
U = len(codes)
|
||||
det = [set() for _ in range(U)]
|
||||
for t in range(T):
|
||||
y = sum(h[u] * amp * Sig[codes[u][0][t]] for u in range(U)) \
|
||||
+ sig * noise_slots[t]
|
||||
resid = y.copy(); support = []
|
||||
for _ in range(U):
|
||||
corr = np.abs(Sig @ resid.conj())
|
||||
if support:
|
||||
corr[support] = -1
|
||||
kk = int(corr.argmax()); support.append(kk)
|
||||
Ah = (Sig[support].T * amp).astype(complex)
|
||||
coef, *_ = np.linalg.lstsq(Ah, y, rcond=None)
|
||||
resid = y - Ah @ coef
|
||||
sset = set(support)
|
||||
for u in range(U):
|
||||
if codes[u][0][t] in sset:
|
||||
det[u].add(codes[u][0][t])
|
||||
recs = []
|
||||
for u in range(U):
|
||||
idx, coef = codes[u]
|
||||
keep = [i for i, tid in enumerate(idx) if tid in det[u]]
|
||||
recs.append(sum(coef[i] * Dict[idx[i]] for i in keep)
|
||||
if keep else None)
|
||||
return recs
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
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")
|
||||
|
||||
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}
|
||||
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]
|
||||
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)) \
|
||||
/ math.sqrt(2)
|
||||
h1, h2 = h
|
||||
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)
|
||||
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])
|
||||
for j in range(2) if recs[j] is not None]
|
||||
if got:
|
||||
res["todma"][k] += float(np.mean(got))
|
||||
cnt["todma"][k] += 1
|
||||
print(f" pair {i+1}/{npairs} done ({time.time()-t0:.0f}s)",
|
||||
flush=True)
|
||||
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))
|
||||
for k, s in enumerate(SNRS):
|
||||
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}) "
|
||||
f"ToDMA {res['todma'][k]:.3f} OMA {res['oma'][k]:.3f} "
|
||||
f"genie {res['genie'][k]:.3f}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,187 @@
|
||||
"""
|
||||
Capacity-matched EDMA refinement (parameter budget equal to the
|
||||
attention scheme: 4 d^2 = 2.36M at d = 768).
|
||||
================================================================
|
||||
Four-head averaged gated refinement applied to the closed-form
|
||||
demultiplexer output:
|
||||
|
||||
out = (1/4) sum_k D softmax(Q_k x / sqrt(D)) .* x,
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import csv
|
||||
import math
|
||||
import time
|
||||
import numpy as np
|
||||
import torch
|
||||
|
||||
from fig_real_merged import load_pairs, cosine, SNRS, NFADE, D, DATA
|
||||
|
||||
SEED = 2026
|
||||
rng = np.random.default_rng(SEED + 31)
|
||||
torch.manual_seed(SEED + 31)
|
||||
BETA0 = 0.028
|
||||
G0 = 1.0 - BETA0**2
|
||||
|
||||
|
||||
def haar_t(gen):
|
||||
Q, R = torch.linalg.qr(torch.randn(D, D, generator=gen))
|
||||
return Q * torch.sign(torch.diagonal(R))
|
||||
|
||||
|
||||
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]
|
||||
opt = torch.optim.Adam(params, lr=lr)
|
||||
stage2_at = 120 # epochs of single-gate pre-training
|
||||
|
||||
def forward(x):
|
||||
outs = [D * torch.softmax((x @ Qk.T) / math.sqrt(D), dim=1) * x
|
||||
for Qk in params]
|
||||
return sum(outs) / len(params)
|
||||
|
||||
t0 = time.time()
|
||||
for ep in range(epochs):
|
||||
if ep == stage2_at:
|
||||
base = params[0].detach()
|
||||
params = [torch.nn.Parameter(
|
||||
base.clone() + 0.02 * torch.randn(D, D, generator=gen)
|
||||
/ 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)
|
||||
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()
|
||||
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:
|
||||
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]
|
||||
|
||||
|
||||
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 main():
|
||||
A, B, betas = load_pairs()
|
||||
P = train_refiner2()
|
||||
ref = np.zeros(len(SNRS)); cnt = 0
|
||||
t0 = time.time()
|
||||
for i in range(len(A)):
|
||||
e1, e2, bi = A[i], B[i], float(betas[i])
|
||||
gi = 1.0 - bi**2
|
||||
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)) \
|
||||
/ math.sqrt(2)
|
||||
h1, h2 = h
|
||||
r0 = h1 * (M1 @ e1) + h2 * (M2 @ e2)
|
||||
n = (rng.standard_normal(D) + 1j * rng.standard_normal(D)) \
|
||||
/ 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))
|
||||
cnt += 1
|
||||
print(f" pair {i+1}/{len(A)} done ({time.time()-t0:.0f}s)",
|
||||
flush=True)
|
||||
ref /= 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 k, r in enumerate(rows):
|
||||
r["edma_ref2"] = f"{ref[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")
|
||||
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}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,41 @@
|
||||
"""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."""
|
||||
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" / "bertvit_merged.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("edma_ref"), "^-", color="C2",
|
||||
label="EDMA + refinement stage")
|
||||
ax.plot(snr, col("todma"), "d-.", color="C4", label="ToDMA-adapted")
|
||||
ax.plot(snr, col("oma"), "v:", color="C1", label="OMA")
|
||||
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.85)
|
||||
ax.legend(loc="upper left")
|
||||
fig.subplots_adjust(**AXES_RECT)
|
||||
fig.savefig(ROOT / "fig" / "fig_bertvit_merged.pdf")
|
||||
print("[OK] wrote fig_bertvit_merged.pdf (no attention curve)")
|
||||
@@ -0,0 +1,43 @@
|
||||
"""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}")
|
||||
@@ -0,0 +1,617 @@
|
||||
"""
|
||||
Revision simulations for the EDMA TCOM resubmission.
|
||||
=======================================================
|
||||
Implements the per-realisation (finite-d) analysis and the corrected
|
||||
energy-normalised rate accounting, plus the reviewer-requested
|
||||
experiments:
|
||||
|
||||
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
|
||||
|
||||
Conventions (identical to the revised 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 <e1,e2> = +beta
|
||||
Fixed seed. CSVs -> ../fig, PDFs -> ../fig_toc.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import csv
|
||||
import math
|
||||
from pathlib import Path
|
||||
import numpy as np
|
||||
import matplotlib
|
||||
matplotlib.use("Agg")
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
CSV_DIR = ROOT / "data"; CSV_DIR.mkdir(exist_ok=True)
|
||||
FIG_DIR = ROOT / "fig"; FIG_DIR.mkdir(exist_ok=True)
|
||||
|
||||
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,
|
||||
"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)
|
||||
|
||||
rng = np.random.default_rng(2026)
|
||||
|
||||
|
||||
def save_fig(fig, name):
|
||||
p = FIG_DIR / f"{name}.pdf"
|
||||
fig.subplots_adjust(**AXES_RECT)
|
||||
fig.savefig(p)
|
||||
plt.close(fig)
|
||||
print(f"[OK] wrote {p}")
|
||||
|
||||
|
||||
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}")
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Core constructions
|
||||
# ------------------------------------------------------------------
|
||||
def haar(d):
|
||||
G = rng.standard_normal((d, d))
|
||||
Q, R = np.linalg.qr(G)
|
||||
return Q * np.sign(np.diag(R))
|
||||
|
||||
|
||||
def unit(v):
|
||||
return v / np.linalg.norm(v)
|
||||
|
||||
|
||||
def embed_pair(d, beta):
|
||||
"""e1, e2 real unit vectors with <e1,e2> = +beta."""
|
||||
e1 = unit(rng.standard_normal(d))
|
||||
w = rng.standard_normal(d)
|
||||
w = unit(w - (w @ e1) * e1)
|
||||
e2 = beta * e1 + math.sqrt(1.0 - beta**2) * w
|
||||
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)), <e1,e2>=+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 cosine(a, b):
|
||||
return abs(np.vdot(a, b)) / (np.linalg.norm(a) * np.linalg.norm(b))
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# E0 : Theorem-1 verification
|
||||
# ------------------------------------------------------------------
|
||||
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 ===")
|
||||
rows = []
|
||||
worst = 0.0
|
||||
for beta in betas:
|
||||
# random unit-modulus channels (AWGN-type magnitude, random phase)
|
||||
errs1, errs2 = [], []
|
||||
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))
|
||||
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}"
|
||||
f" (max dev {dev:.2f}%)")
|
||||
rows.append([beta, r1, r2, dev])
|
||||
write_csv("theorem_check", ["beta", "user1_mc_over_theory",
|
||||
"user2_mc_over_theory", "max_dev_pct"], rows)
|
||||
print(f" worst-case deviation {worst:.2f}%")
|
||||
return worst
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# E1 : interference floor (MSE vs block SNR), AWGN point |h|=1
|
||||
# ------------------------------------------------------------------
|
||||
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 ===")
|
||||
g = 1.0 - beta**2
|
||||
csi = C_SI(beta, 1.0 + 0j)
|
||||
fig, ax = plt.subplots()
|
||||
colors = {256: "C0", 768: "C3"}
|
||||
rows = []
|
||||
for d in dims:
|
||||
mc = np.zeros(len(snr_db))
|
||||
for _ in range(ntr):
|
||||
e1, e2 = embed_pair(d, beta)
|
||||
M1, M2 = two_user_masks(d, beta)
|
||||
r0 = (M1 @ e1) + (M2 @ 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(r0 + sig * n, M1, M2, 1.0, 1.0, beta)
|
||||
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)
|
||||
ax.semilogy(snr_db, mc, "o", ms=3.5, color=colors[d], mfc="none",
|
||||
label=rf"MC, $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.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.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)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# E2 : realisable SIC vs genie SIC vs EDMA vs OMA (Rayleigh)
|
||||
# ------------------------------------------------------------------
|
||||
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}")
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 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 E7_rates(beta=0.311, d=512):
|
||||
print("\n=== E7a: corrected effective-rate comparison ===")
|
||||
snr_db = np.arange(0, 31, 1.0)
|
||||
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)
|
||||
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.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.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]]
|
||||
for i, s in enumerate(snr_db)]
|
||||
write_csv("rate_corrected",
|
||||
["snr_db", "edma", "edma_ideal", "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("\n=== E7b: corrected beta 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)
|
||||
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)")
|
||||
for i, b in enumerate(betas):
|
||||
rows.append([s, b, Te[i], To, Tg])
|
||||
for b0 in (0.031, 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)
|
||||
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 <e_u,e_v> ~ 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)
|
||||
|
||||
|
||||
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,
|
||||
}
|
||||
for name, fn in ALL.items():
|
||||
if not todo or name in todo:
|
||||
fn()
|
||||
print("\nAll requested revision simulations complete.")
|
||||
@@ -0,0 +1,374 @@
|
||||
"""
|
||||
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.
|
||||
|
||||
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
|
||||
"""
|
||||
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}")
|
||||
|
||||
|
||||
def haar(d):
|
||||
Q, R = np.linalg.qr(rng.standard_normal((d, d)))
|
||||
return Q * np.sign(np.diag(R))
|
||||
|
||||
|
||||
def unit(v):
|
||||
return v / np.linalg.norm(v)
|
||||
|
||||
|
||||
def 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
|
||||
|
||||
|
||||
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 csi2(beta, c):
|
||||
g = 1 - beta**2
|
||||
return (abs(c)**2 + beta**2 + 2 * beta**2 * np.real(c)) / g
|
||||
|
||||
|
||||
# ---------------- 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}")
|
||||
|
||||
# ---------------- 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}%")
|
||||
|
||||
# ---------------- V4: quoted constants -----------------------------
|
||||
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)")
|
||||
|
||||
# ---------------- 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)
|
||||
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})")
|
||||
|
||||
# ---------------- 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}")
|
||||
|
||||
print()
|
||||
print("=" * 60)
|
||||
print(f"RESULT: {'ALL PASS' if not FAIL else 'FAILURES: ' + ', '.join(FAIL)}")
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
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
|
||||
|
@@ -0,0 +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
|
||||
|
@@ -0,0 +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 +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
|
||||
|
@@ -0,0 +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
|
||||
|
@@ -0,0 +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
|
||||
|
@@ -0,0 +1,32 @@
|
||||
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
|
||||
|
@@ -0,0 +1,8 @@
|
||||
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
|
||||
|
@@ -0,0 +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
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user