Files
TMC/code/exp1_theory.py

108 lines
4.2 KiB
Python

"""E1: receiver theory verification (synthetic isotropic contents).
Outputs: data/e1_beta.csv, data/e1_snr.csv, data/e1_endpoints.txt
(figures come from replot_all.py only)
"""
import math
import os
import numpy as np
from semantic_mac import (affinity_matrix, matched_filter, demux_sr, demux_sc,
demux_lmmse, demux_dr, lmmse_matrices,
sample_latents_isotropic, metrics,
oma_observe, demux_noma_genie)
HERE = os.path.dirname(os.path.abspath(__file__))
FIG = os.path.join(HERE, "..", "fig")
DATA = os.path.join(HERE, "..", "data")
os.makedirs(FIG, exist_ok=True)
os.makedirs(DATA, exist_ok=True)
U, D, DC = 4, 64, 16
BATCH = 4000
NAMES = ["OMA", "NOMA", "SR", "SC", "LMMSE", "DR"]
def run_point(beta, rho, rng, conv_seed=0):
a = math.sqrt(beta) * np.ones(U)
B = affinity_matrix(a)
z = sample_latents_isotropic(BATCH, U, D, DC, a, rng)
tilde, h = matched_filter(z, B, rho, rng)
Vc = np.eye(D)[:, :DC]
res = {}
res["SR"] = metrics(demux_sr(tilde, B, h), z)
res["SC"] = metrics(demux_sc(tilde, h), z)
lm, cf = demux_lmmse(tilde, B, h, rho)
res["LMMSE"] = metrics(lm, z)
res["LMMSE_cf"] = float(cf.mean())
# SR closed form: d sigma^2 [B^-1]_uu / h^2 averaged
Binv = np.diag(np.linalg.inv(B))
res["SR_cf"] = float((D / rho) * (Binv[None, :] / h ** 2).mean())
res["DR"] = metrics(demux_dr(tilde, B, h, rho, a, Vc), z)
# conventional baselines on a dedicated stream (keeps main draws intact)
rng_c = np.random.default_rng(90000 + conv_seed)
res["OMA"] = metrics(oma_observe(z, h, rho, rng_c), z)
res["NOMA"] = metrics(demux_noma_genie(z, h, rho, rng_c), z)
return res
def main():
rng = np.random.default_rng(0)
rho_db = 10
rho = 10 ** (rho_db / 10)
betas = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.95, 0.99]
rows = []
for b in betas:
r = run_point(b, rho, rng, conv_seed=int(b * 100))
rows.append(r)
print(f"beta={b:4.2f} " + " ".join(
f"{n}:cos={r[n][0]:.3f},nmse={r[n][1]:.3f},ser={r[n][2]:.3f}"
for n in NAMES))
with open(os.path.join(DATA, "e1_beta.csv"), "w") as f:
f.write("beta," + ",".join(f"{n}_cos,{n}_nmse,{n}_ser" for n in NAMES)
+ ",LMMSE_cf,SR_cf\n")
for b, r in zip(betas, rows):
f.write(f"{b}," + ",".join(
f"{r[n][0]},{r[n][1]},{r[n][2]}" for n in NAMES)
+ f",{r['LMMSE_cf']},{r['SR_cf']}\n")
beta_mid = 0.4
snrs = list(range(0, 21, 4))
rows_s = []
for s in snrs:
r = run_point(beta_mid, 10 ** (s / 10), rng, conv_seed=1000 + s)
rows_s.append(r)
print(f"snr={s} " + " ".join(f"{n}:ser={r[n][2]:.3f}" for n in NAMES))
with open(os.path.join(DATA, "e1_snr.csv"), "w") as f:
f.write("snr," + ",".join(f"{n}_cos,{n}_nmse,{n}_ser" for n in NAMES) + "\n")
for s, r in zip(snrs, rows_s):
f.write(f"{s}," + ",".join(
f"{r[n][0]},{r[n][1]},{r[n][2]}" for n in NAMES) + "\n")
# endpoint checks
lines = []
a = math.sqrt(0.4) * np.ones(U)
B = affinity_matrix(a)
h = np.clip(np.abs((rng.standard_normal(U) + 1j * rng.standard_normal(U))
/ math.sqrt(2)), 0.2, None)
for rdb in (10, 40, 80):
W, _ = lmmse_matrices(B, h, 10 ** (-rdb / 10), D)
Ginv = np.linalg.inv(np.diag(1 / h) @ B @ np.diag(h))
rel = np.linalg.norm(W - Ginv) / np.linalg.norm(Ginv)
lines.append(f"(i) rho={rdb}dB rel_diff_W_vs_Gammainv={rel:.3e}")
W0, _ = lmmse_matrices(np.eye(U), h, 0.1, D)
off = np.abs(W0 - np.diag(np.diag(W0))).max()
wiener = h ** 2 / (h ** 2 + D * 0.1)
lines.append(f"(ii) beta=0 max_offdiag={off:.3e} "
f"max_diag_minus_wiener={np.abs(np.diag(W0)-wiener).max():.3e}")
with open(os.path.join(DATA, "e1_endpoints.txt"), "w") as f:
f.write("\n".join(lines) + "\n")
print("\n".join(lines))
# figures are produced only by the canonical replot_all.py (uniform
# geometry); experiment scripts write CSVs exclusively.
print("E1 done. Run replot_all.py to regenerate the figures.")
if __name__ == "__main__":
main()