Semantic Multiplexing Gain in Wireless Systems via Expanded Embeddings: A BERT Case Study. Includes the shared library, all experiment scripts (training with and without SNR-aware MAML, the token-domain comparison, the K sweep, and DistilBERT), the replot script that regenerates every figure from the stored results, the supplementary probe-versus-cosine analysis, and the raw results behind every figure in the letter.
184 lines
7.6 KiB
Python
Executable File
184 lines
7.6 KiB
Python
Executable File
# Plot CL-letter figures from fig_cl/cl_results*.json
|
|
#
|
|
# Geometry rule (paper_requirement): every result figure uses the same
|
|
# fixed canvas and the same 8:6 axes box, and no tight bounding box is
|
|
# applied at save time. Scheme names avoid the banned word "baseline".
|
|
#
|
|
# Figure set (single large graph per figure):
|
|
# Fig. 2 (cl_fig_mux.pdf) : one SNR sweep merging the load sweep
|
|
# (conventional + proposed U=1..4) and the
|
|
# matched-budget comparison (random mask,
|
|
# ToDMA x2) at U=4.
|
|
# Fig. 3 (cl_fig_agg.pdf) : aggregate fidelity bars for U=1..6 with
|
|
# per-user CosSim and the fully loaded
|
|
# orthogonal reference.
|
|
import json
|
|
import numpy as np
|
|
import matplotlib
|
|
matplotlib.use("Agg")
|
|
import matplotlib.pyplot as plt
|
|
plt.rcParams.update({'font.size': 15, 'axes.linewidth': 1.2})
|
|
|
|
with open("fig_cl/cl_results.json") as f:
|
|
R = json.load(f)
|
|
# MAML-trained results (reported default protocol) overlay the joint
|
|
# runs: every reported transceiver key is remapped to its MAML twin.
|
|
with open("fig_cl/cl_results_maml.json") as f:
|
|
R.update(json.load(f))
|
|
try:
|
|
with open("fig_cl/cl_results_maml2.json") as f:
|
|
R.update(json.load(f))
|
|
except FileNotFoundError:
|
|
pass
|
|
KEYMAP = {
|
|
"baseline_U1_K1": "mamlB_U1_K1",
|
|
"prop_U1_K4": "mamlP_U1_K4", "prop_U2_K4": "mamlP_U2_K4",
|
|
"prop_U3_K4": "mamlP_U3_K4", "prop_U4_K4": "mamlP_U4_K4",
|
|
"prop_U5_K4": "mamlP_U5_K4", "prop_U6_K4": "mamlP_U6_K4",
|
|
"randmask_U4_K4": "mamlR_U4_K4",
|
|
"ksweep_U4_K1": "mamlK_U4_K1", "ksweep_U4_K2": "mamlK_U4_K2",
|
|
"ksweep_U4_K8": "mamlK_U4_K8",
|
|
}
|
|
# Preserve the joint-trained runs before remapping: they appear in the
|
|
# figures as the joint-training ablation (same architecture, no MAML).
|
|
JOINT = {k: R[k] for k in list(KEYMAP.keys()) if k in R}
|
|
for old, new in KEYMAP.items():
|
|
if new in R:
|
|
R[old] = R[new]
|
|
|
|
SNRS = [0, 5, 10, 15, 20, 25, 30]
|
|
|
|
# Single-graph geometry shared by both result figures: canvas
|
|
# 7.5 x 5.55 in, axes box 6.0 x 4.5 in (exactly 8:6).
|
|
FIGSIZE = (7.5, 5.55)
|
|
AX_RECT = [0.105, 0.115, 0.77, 0.7804]
|
|
|
|
|
|
def one_panel():
|
|
fig = plt.figure(figsize=FIGSIZE)
|
|
return fig, fig.add_axes(AX_RECT)
|
|
|
|
|
|
def cos_curve(key):
|
|
return [R[key]["snr"][str(s)]["cos"] for s in SNRS]
|
|
|
|
|
|
# ============================================================
|
|
# Fig 2: merged SNR sweep (load sweep + matched-budget comparison)
|
|
# ============================================================
|
|
fig, ax = one_panel()
|
|
|
|
curves = [
|
|
("prop_U1_K4", "Proposed $U$=1", "s", "-", "#1a9641", 2),
|
|
("prop_U2_K4", "Proposed $U$=2", "^", "-", "#2166ac", 2),
|
|
("prop_U3_K4", "Proposed $U$=3", "D", "-", "#d95f02", 2),
|
|
("prop_U4_K4", "Proposed $U$=4", "v", "-", "#d62728", 2.5),
|
|
("JOINT:prop_U4_K4", "Training w/o MAML [5]", "x",
|
|
(0, (5, 2)), "#8c564b", 2),
|
|
("baseline_U1_K1", "Conventional orthogonal", "o", "--", "#1a1a1a", 2),
|
|
("randmask_U4_K4", "Random-projection mask", "s", "-.", "#984ea3", 2),
|
|
("todma_T24_L128", "ToDMA $24\\times128$", "^", ":",
|
|
"#4393c3", 2),
|
|
("todma_T16_L192", "ToDMA $16\\times192$", "D", ":",
|
|
"#92c5de", 2),
|
|
]
|
|
for key, lab, mk, ls, col, lw in curves:
|
|
if key.startswith("todma"):
|
|
vals = [R[key][str(s)]["cos"] for s in SNRS]
|
|
elif key.startswith("JOINT:"):
|
|
vals = [JOINT[key[6:]]["snr"][str(s)]["cos"] for s in SNRS]
|
|
else:
|
|
vals = cos_curve(key)
|
|
ax.plot(SNRS, vals, marker=mk, linestyle=ls, color=col,
|
|
linewidth=lw, markersize=8, label=lab)
|
|
ax.set_xlabel("SNR (dB)", fontsize=17)
|
|
ax.set_ylabel("Cosine Similarity", fontsize=17)
|
|
ax.set_ylim([0.45, 1.0])
|
|
ax.legend(fontsize=12.5, loc="lower right", ncol=1)
|
|
ax.grid(True, alpha=0.3)
|
|
fig.savefig("fig_cl/cl_fig_mux.pdf", dpi=200)
|
|
fig.savefig("fig_cl/cl_fig_mux.png", dpi=150)
|
|
plt.close(fig)
|
|
print("Saved cl_fig_mux.pdf")
|
|
|
|
# ============================================================
|
|
# Fig 3: aggregate fidelity across load (bars + per-user line)
|
|
# ============================================================
|
|
fig, ax2 = one_panel()
|
|
|
|
from matplotlib.patches import Patch
|
|
|
|
snr_show = "10"
|
|
conv_cos = R["baseline_U1_K1"]["snr"][snr_show]["cos"]
|
|
per_user = [R[f"prop_U{U}_K4"]["snr"][snr_show]["cos"] for U in range(1, 7)]
|
|
joint_pu = [JOINT[f"prop_U{U}_K4"]["snr"][snr_show]["cos"]
|
|
for U in range(1, 7)]
|
|
thr = [U * c for U, c in zip(range(1, 7), per_user)]
|
|
thr_j = [U * c for U, c in zip(range(1, 7), joint_pu)]
|
|
xs = np.arange(1, 7)
|
|
# Single color per scheme so the bars match the legend patches.
|
|
C_PROP = "#d62728"
|
|
with open("fig_cl/cl_results_todma_u.json") as f:
|
|
RT = json.load(f)
|
|
todma_pu = [RT[f"todma_U{U}_T24_L128"][snr_show]["cos"] if U != 4
|
|
else R["todma_T24_L128"][snr_show]["cos"] for U in range(1, 7)]
|
|
thr_t = [U * c for U, c in zip(range(1, 7), todma_pu)]
|
|
ax2.bar([0], [conv_cos], width=0.55, color="#1a1a1a", alpha=0.85)
|
|
ax2.text(0, conv_cos + 0.08, f"{conv_cos:.2f}", ha="center",
|
|
va="bottom", fontsize=12)
|
|
ax2.bar(xs - 0.27, thr, width=0.26, color=C_PROP, alpha=0.9)
|
|
ax2.bar(xs, thr_j, width=0.26, color=C_PROP, alpha=0.4,
|
|
hatch="//", edgecolor="#555555", linewidth=0.5)
|
|
ax2.bar(xs + 0.27, thr_t, width=0.26, color="#4393c3", alpha=0.75,
|
|
hatch="..", edgecolor="#1f5f8b", linewidth=0.5)
|
|
for x, val in zip(xs, thr):
|
|
ax2.text(x - 0.27, val + 0.08, f"{val:.2f}", ha="center",
|
|
va="bottom", fontsize=11)
|
|
# Fully loaded orthogonal aggregate (4 blocks x 768 uses = same budget)
|
|
ax2.axhline(4 * conv_cos, linestyle="-.", color="#555555", linewidth=2)
|
|
ax2.text(-0.45, 4 * conv_cos + 0.13, "Fully loaded orthogonal",
|
|
fontsize=12.5, color="#555555")
|
|
ax2.set_xticks([0] + list(xs))
|
|
ax2.set_xticklabels(["Conv.\n$U$=1"] + [f"Multi.\n$U$={U}"
|
|
for U in range(1, 7)], fontsize=12)
|
|
ax2.set_ylabel(r"Aggregate fidelity ($U \!\cdot\! \mathrm{CosSim}$)",
|
|
fontsize=16)
|
|
ax2.grid(True, alpha=0.3, axis="y")
|
|
ax2.set_ylim([0, max(thr) * 1.22])
|
|
handles = [Patch(facecolor=C_PROP, alpha=0.9, label="Proposed"),
|
|
Patch(facecolor=C_PROP, alpha=0.4, hatch="//",
|
|
edgecolor="#555555", label="Training w/o MAML [5]"),
|
|
Patch(facecolor="#4393c3", alpha=0.75, hatch="..",
|
|
edgecolor="#1f5f8b", label="ToDMA $24\\times128$")]
|
|
ax2.legend(handles=handles, fontsize=11, loc="center left",
|
|
bbox_to_anchor=(0.02, 0.44))
|
|
fig.savefig("fig_cl/cl_fig_agg.pdf", dpi=200)
|
|
fig.savefig("fig_cl/cl_fig_agg.png", dpi=150)
|
|
plt.close(fig)
|
|
print("Saved cl_fig_agg.pdf")
|
|
|
|
# ============================================================
|
|
# Print the numbers quoted in the letter
|
|
# ============================================================
|
|
print("\n===== NUMBERS FOR TEXT (MAML default, held-out) =====")
|
|
print("conv per-user@20:", round(conv_cos, 3),
|
|
" fully loaded aggregate:", round(4 * conv_cos, 2))
|
|
for U in range(1, 7):
|
|
v = R[f"prop_U{U}_K4"]["snr"]["20"]["cos"]
|
|
print(f"U={U}: per-user {v:.3f} aggregate {U*v:.2f}")
|
|
print("overload ratio:",
|
|
round(6 * R["prop_U6_K4"]["snr"]["20"]["cos"] / (4 * conv_cos), 2))
|
|
print("randmask@20:", round(R["randmask_U4_K4"]["snr"]["20"]["cos"], 3))
|
|
for s in ["0", "5", "15", "20", "30"]:
|
|
print(f"todma24@{s}: {R['todma_T24_L128'][s]['cos']:.3f} "
|
|
f"prop@{s}: {R['prop_U4_K4']['snr'][s]['cos']:.3f}")
|
|
for k in ["ksweep_U4_K1", "ksweep_U4_K2", "prop_U4_K4", "ksweep_U4_K8"]:
|
|
print(k, "0dB:", round(R[k]["snr"]["0"]["cos"], 3),
|
|
"20dB:", round(R[k]["snr"]["20"]["cos"], 3))
|
|
if "mamlD_U4_K4" in R:
|
|
print("distil(MAML)@20:", round(R["mamlD_U4_K4"]["snr"]["20"]["cos"], 3))
|
|
|
|
|
|
|
|
|