Code and stored results for the IEEE Communications Letters submission
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.
This commit is contained in:
Executable
+183
@@ -0,0 +1,183 @@
|
||||
# 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))
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user