Restructure package: descriptive study documentation and clean layout

This commit is contained in:
Ki-Ho Lee
2026-08-25 20:25:46 +09:00
parent 7e831474af
commit 1fc9cad834
38 changed files with 397 additions and 2262 deletions
+43 -36
View File
@@ -2,60 +2,67 @@
Simulation code, raw results, and figure sources for the manuscript
"Relevance-Aware Semantic Multiple Access via Meta-Learned User-Wise
Cross-Attention" (submitted to IEEE Transactions on Wireless Communications).
Cross-Attention" (submitted to the IEEE Transactions on Communications).
The package contains everything needed to regenerate every number and
figure in the manuscript: one script per study, the raw JSON/CSV results
each script produced, and a single plotting script that rebuilds the figure
PDFs from the stored results without rerunning any experiment.
**Detailed per-study documentation (question, setup, procedure, metrics,
key results): [docs/EXPERIMENTS.md](docs/EXPERIMENTS.md).**
## Requirements
- Python 3.10+ with `torch` (CUDA optional; results generated on an NVIDIA
RTX A4500, PyTorch 2.10, CUDA 12.8), `numpy`, `matplotlib`, and
- Python 3.10+ with `torch` (results generated on an NVIDIA RTX A4500,
PyTorch 2.10, CUDA 12.8; CPU fallback works), `numpy`, `matplotlib`, and
`scikit-learn` (real-data study only).
- All experiments use fixed seed 42 (auxiliary generators seeded as noted in
each script).
- All experiments use fixed seed 42 (auxiliary generators seeded as noted
in each script).
## Layout
- `rev2/lib.py` — shared library: single-superimposed-signal channel
(Rayleigh / Rician / Nakagami fading, complex phase residuals, timing
offsets, CSI error), UWCA decoder (active-set masking, top-k masking,
I/Q input), closed-form LMMSE receivers, training loops (multi-task
meta-training and first-order MAML), evaluation metrics.
- `rev2/e1_fair_baselines.py``rev2/e9_topk_online.py` — one script per
experiment (see the table below). `*_v2/_v3` scripts supersede their
earlier versions where present.
- `rev2/data/` — raw JSON results behind every quoted number.
- `rev2/plot_rev2.py` — regenerates every new figure from `rev2/data/`
without rerunning experiments.
- `legacy/` — scripts for the pre-revision figures (synthetic three-scenario
study, attention maps, beta sweep, real-data study).
- `experiments/lib.py` — shared library: the single-superimposed-signal
uplink of the manuscript (Rayleigh / Rician / Nakagami fading, complex
phase residuals, timing offsets, CSI error; fixed disjoint transmit block
masks), the UWCA decoder (learned soft masks, active-set masking, top-k
masking, optional I/Q input), closed-form LMMSE reference receivers,
training loops, and evaluation metrics.
- `experiments/e1_fair_baselines.py``experiments/e9_topk_online.py`
one self-contained script per study (see the table below).
- `experiments/data/` — raw JSON results behind every quoted number.
- `experiments/make_figures.py` — regenerates the figure PDFs from
`experiments/data/` only.
- `simulation/` — the three-scenario synthetic study, hyperparameter
ablations, threshold sweeps, and the real-data study.
- `fig/` — figure PDFs as included in the manuscript.
**Detailed per-experiment documentation (setup, procedure, metrics, key results, reviewer-concern map): [docs/EXPERIMENTS.md](docs/EXPERIMENTS.md).**
## Figure/number → script → data map
## Study → script → data map
| Manuscript item | Script | Data |
|---|---|---|
| Fig. SER vs SNR (3 scenarios) | `legacy/semantic_correlation_sim.py`, `legacy/maml_semantic.py` | `legacy/results/` |
| Fig. fairness vs optimal linear receivers | `rev2/e1_fair_baselines.py` | `rev2/data/e1_fair_baselines.json` |
| Fig. residual orthogonality | `rev2/e6_residual_orth.py` | `rev2/data/e6_residual_orth.json` |
| Phase-error robustness (complex model, CSI error) | `rev2/e2_phase_iui.py` | `rev2/data/e2_phase_iui.json` |
| Timing-offset robustness | `rev2/e4_v3_async.py` | `rev2/data/e4_v3_async.json` |
| Dynamic user population | `rev2/e3_dynamic_users.py` | `rev2/data/e3_dynamic_users.json` |
| Nonlinear view-network study | `rev2/e5_nonlinear.py` | `rev2/data/e5_nonlinear.json` |
| Meta-adaptation beyond SNR (OOD) | `rev2/e7_v2_meta.py` | `rev2/data/e7_v2_meta.json` |
| End-to-end anti-collapse study | `rev2/e8_v2_e2e.py` | `rev2/data/e8_v2_e2e.json` |
| Online top-k acquisition and timing | `rev2/e9_topk_online.py` | `rev2/data/e9_topk_online.json` |
| Real-data validation | `legacy/revision_realdata_train.py`, `legacy/revision_realdata_plot.py` | `legacy/results/` |
| SER vs SNR, three scenarios (Fig. 2) | `simulation/semantic_correlation_sim.py`, `simulation/maml_semantic.py` | `simulation/results/` |
| Optimal-linear-receiver fairness (Fig. 3) | `experiments/e1_fair_baselines.py` | `experiments/data/e1_fair_baselines.json` |
| Residual orthogonality (Fig. 4) | `experiments/e6_residual_orth.py` | `experiments/data/e6_residual_orth.json` |
| Complex phase-error model, CSI error | `experiments/e2_phase_iui.py` | `experiments/data/e2_phase_iui.json` |
| Timing offsets and realignment | `experiments/e4_async.py` | `experiments/data/e4_async.json` |
| Dynamic user population | `experiments/e3_dynamic_users.py` | `experiments/data/e3_dynamic_users.json` |
| Nonlinear view-network study | `experiments/e5_nonlinear.py` | `experiments/data/e5_nonlinear.json` |
| Adaptation across fading families | `experiments/e7_meta.py` | `experiments/data/e7_meta.json` |
| End-to-end training with anti-collapse | `experiments/e8_e2e.py` | `experiments/data/e8_e2e.json` |
| Online top-k acquisition and timing | `experiments/e9_topk_online.py` | `experiments/data/e9_topk_online.json` |
| beta sweep, attention maps, ablations | `simulation/revision_*.py`, `simulation/plot_figures.py` | `simulation/results/` |
| Real-data validation (Fig. 5) | `simulation/revision_realdata_train.py`, `simulation/revision_realdata_plot.py` | `simulation/results/` |
## Running
```bash
python rev2/e1_fair_baselines.py # writes rev2/data/e1_fair_baselines.json
python rev2/plot_rev2.py # regenerates the new figure PDFs
python experiments/e1_fair_baselines.py # writes experiments/data/e1_fair_baselines.json
python experiments/make_figures.py # rebuilds the figure PDFs from data/
```
Each experiment script is self-contained and writes its JSON into
`rev2/data/`.
Each study script is self-contained and writes its JSON into
`experiments/data/`.
## Citation and license
+172 -178
View File
@@ -1,20 +1,21 @@
# Experiment Documentation
# Study Documentation
Detailed description of every experiment behind the manuscript
Detailed description of every study behind the manuscript
"Relevance-Aware Semantic Multiple Access via Meta-Learned User-Wise
Cross-Attention." Each entry states the reviewer concern it addresses, the
exact setup, the procedure, the metrics, the key results, and the artifact
paths. All experiments run on a single NVIDIA RTX A4500 (PyTorch 2.10, CUDA
12.8), seed 42, and write raw JSON results consumed only by
`rev2/plot_rev2.py`.
Cross-Attention": the question each study answers, the exact setup, the
procedure, the metrics, the key results, and the artifact paths. All
studies run on a single NVIDIA RTX A4500 (PyTorch 2.10, CUDA 12.8), seed
42, and write raw JSON results consumed only by
`experiments/make_figures.py`.
**Common system model** (all rev2 experiments, matching manuscript Eq. (7)):
single superimposed uplink y = sum_v |h_v| e^{j dphi_v} (e_v (.) m_v) + n over
one d=64-dimensional frame; fixed {0,1} disjoint transmit block masks
(d/U coordinates per user, identical for every compared scheme); per-rail
noise std = sqrt(mean|y_tx|^2 / SNR); U=4 users, HIGH/LOW/MIX scenarios of
Table II unless stated. The decoder-side masks are separate learned soft
masks in [0,1]^d initialized at the block pattern. Training is the
**Common system model** (all `experiments/` studies, matching the
manuscript's received-signal model): single superimposed uplink
y = sum_v |h_v| e^{j dphi_v} (e_v (.) m_v) + n over one d=64-dimensional
frame; fixed {0,1} disjoint transmit block masks (d/U coordinates per user,
identical for every compared scheme); per-rail noise
std = sqrt(mean|y_tx|^2 / SNR); U=4 users and the HIGH/LOW/MIX scenarios of
the manuscript unless stated. The decoder-side masks are separate learned
soft masks in [0,1]^d initialized at the block pattern. Training is the
manuscript's first-order meta-training aggregated over SNR tasks
{0,4,...,20} dB, Adam 1e-3 (mask logits 0.1), 250-300 epochs, batch 64;
evaluation uses 150-200 Monte Carlo batches of 64 per point.
@@ -22,225 +23,218 @@ evaluation uses 150-200 Monte Carlo batches of 64 per point.
---
## E1 — Comparison fairness against optimal linear receivers
*Reviewer concerns: R1-10, R3-7 (d/U comparison "mathematically unfair");
editor point 4.*
- **Question.** Is the OFDMA/SFDMA subspace ceiling an artifact of weak
baselines, and how much of the UWCA gain is mere dimensionality?
- **Question.** Is the d/U subspace ceiling of orthogonal access an
artifact of weak comparison schemes, and how much of the UWCA gain is
mere dimensionality rather than relevance exploitation?
- **Setup.** For each scenario the true relevance matrix B (B_uv =
beta_u beta_v for shared scene, else 0) and the per-realization channel
beta_u beta_v for a shared scene, else 0) and the per-realization channel
magnitudes are formed. Receivers evaluated on the *same* received frame:
`uwca` (trained), `ofdma`, `sfdma`, `noma` (full-band power-domain SIC,
powers 0.40/0.30/0.20/0.10), `lmmse_blind` (closed-form Wiener with
cross-covariances zeroed), `lmmse_genie` (closed form with true B and
channel gains, manuscript Eq. (23)), `tdma_proj` (random orthonormal
16-dim projection per user — an arbitrary orthogonal partition).
channel gains, the manuscript's Proposition on optimal linear receivers),
and `tdma_proj` (random orthonormal 16-dim projection per user — an
arbitrary orthogonal partition).
- **Key results.** lmmse_blind = OFDMA at every SNR in all scenarios
(SER 0.509 vs 0.508 @10 dB HIGH), tdma_proj and SFDMA coincide with them;
HIGH @20 dB: blind 0.327 -> UWCA 0.110 -> genie 0.044 (UWCA recovers 77%
of the blind-to-genie gap); LOW: genie = blind (nothing to exploit),
UWCA within 0.02 SER.
- **Artifacts.** `rev2/e1_fair_baselines.py` ->
`rev2/data/e1_fair_baselines.json` -> `fig/fig_fair.pdf` (manuscript
Fig. 3). Trained checkpoints `e1_uwca_{HIGH,LOW,MIX}.pt` (reused by E4/E6).
(SER 0.509 vs 0.508 @10 dB HIGH), and tdma_proj and SFDMA coincide with
them — the ceiling binds every correlation-blind receiver. HIGH @20 dB:
blind 0.327 -> UWCA 0.110 -> genie 0.044, so UWCA recovers 77% of the
blind-to-genie gap without side information. LOW: genie = blind (nothing
to exploit), UWCA within 0.02 SER.
- **Artifacts.** `experiments/e1_fair_baselines.py` ->
`experiments/data/e1_fair_baselines.json` -> `fig/fig_fair.pdf`
(manuscript Fig. 3). Trained checkpoints `e1_uwca_{HIGH,LOW,MIX}.pt`
(reused by E4/E6).
## E2 — Full complex-baseband phase errors and CSI error
*Reviewer concerns: R1-1 (phase model underestimates IUI), R2-3 (CSI).*
- **Question.** Does the multi-user superposition amplify residual phase
errors into severe inter-user interference once nothing is absorbed into a
noise term?
errors into severe inter-user interference once every leakage path is
simulated explicitly?
- **Setup.** Complex channel with per-user residual dphi_u ~ N(0, sigma^2),
sigma in {0,5,10,15,20,30} deg; both rails simulated so every leakage path
exists. Three decoders: mismatch-trained (never saw phase errors),
phase-augmented (trained with sigma ~ U[0,20] deg), and a two-rail variant
whose keys/values read [Re;Im] (2d input). CSI sweep: amplitude error
h_hat = h(1+eps), eps ~ N(0, sigma_h^2), sigma_h up to 0.2, at 10 deg.
Learned-mask overlap (mean pairwise cosine) is also measured.
sigma in {0,5,10,15,20,30} deg; both rails simulated. Three decoders:
mismatch-trained (never saw phase errors), phase-augmented (trained with
sigma ~ U[0,20] deg), and a two-rail variant whose keys/values read
[Re;Im]. CSI sweep: amplitude error h_hat = h(1+eps),
eps ~ N(0, sigma_h^2), sigma_h up to 0.2, at 10 deg. The learned-mask
overlap (mean pairwise cosine) is also measured.
- **Key results.** Mismatch-trained @10 dB: SER 0.270 -> 0.291 (20 deg) ->
0.332 (30 deg); @20 dB 0.110 -> 0.127; augmentation and I/Q reading change
curves by less than Monte Carlo spread -> leakage is second order because
it scales with sin(dphi) x mask overlap (measured 0.26). CSI: UWCA
unchanged within 0.004 (uses no explicit CSI).
- **Artifacts.** `rev2/e2_phase_iui.py` -> `rev2/data/e2_phase_iui.json`;
`fig/fig_phase2.pdf` (repo; numbers narrated in manuscript Sec. VII-E).
0.332 (30 deg); @20 dB 0.110 -> 0.127; augmentation and I/Q reading
change the curves by less than the Monte Carlo spread. The leakage is
second order because it scales with sin(dphi) times the mask overlap
(measured 0.26). CSI: UWCA unchanged within 0.004 (it uses no explicit
CSI).
- **Artifacts.** `experiments/e2_phase_iui.py` ->
`experiments/data/e2_phase_iui.json`; `fig/fig_phase2.pdf`.
## E3 — Dynamic user population
*Reviewer concerns: R1-6 (fixed U, costly retraining), R2-3 (fixed
identities).*
- **Setup.** U_max = 8 mask slots (d=64, 8 dims/slot), HIGH-type correlation
(beta_u = 0.6, one scene). Active set resampled per frame; inactive users
transmit nothing and their softmax scores are masked to -inf (the
scheduler announces the active set). Compared: one model trained with the
full population always active (never retrained), an activity-sampled
model, and per-count oracle models retrained from scratch for
- **Question.** Does a decoder provisioned for U_max users need retraining
when users arrive and depart?
- **Setup.** U_max = 8 mask slots (d=64, 8 dims/slot), high-correlation
scenario (beta_u = 0.6, one scene). The active set is resampled per
frame; inactive users transmit nothing and their softmax scores are
masked out (the scheduler announces the active set). Compared: one model
trained with the full population always active (never retrained), an
activity-sampled model, and per-count models retrained from scratch for
|A| in {2,4,6,8}. Metric: mean cosine (the sqrt(1/8)=0.35 subspace
ceiling saturates the binary SER at this scale).
- **Key results.** The single full-population model sustains fidelity over
every active count (c 0.34 -> 0.39 for |A| = 2 -> 8 @10 dB) and matches or
exceeds the per-count retrained models (0.33/0.32/0.36/0.39); per-count
retraining is counterproductive (sparse populations train each slot on a
fraction of the traffic).
- **Artifacts.** `rev2/e3_dynamic_users.py` ->
`rev2/data/e3_dynamic_users.json`; `fig/fig_dynusers.pdf` (repo).
every active count (c 0.34 -> 0.39 for |A| = 2 -> 8 @10 dB) and matches
or exceeds the per-count retrained models (0.33/0.32/0.36/0.39);
per-count retraining is counterproductive, since sparse populations train
each slot on a fraction of the traffic.
- **Artifacts.** `experiments/e3_dynamic_users.py` ->
`experiments/data/e3_dynamic_users.json`; `fig/fig_dynusers.pdf`.
## E4 — Symbol-timing offsets (v3 = block-wise realignment)
*Reviewer concern: R1-8 (asynchronous reception / ISI).*
## E4 — Symbol-timing offsets and receiver-side realignment
- **Question.** How does the decoder behave under asynchronous reception,
and what does the standard timing-correction chain restore?
- **Setup.** Per-user integer offsets delta_u ~ U{0..Delta} symbols,
Delta in {0,1,2,4,8}, shift each user's transmitted block within the
frame (edge energy lost). Conditions: uncorrected (UWCA and OFDMA),
corrected by block-wise realignment using pilot-estimated offsets
(each user's block region shifted back individually), and corrected with
a deliberately impaired estimator (+-1 symbol on 20% of users).
v1 (`e4_async` results in json) showed offset-augmented *training* cannot
repair unknown shifts; v2 showed whole-frame realignment breaks
cross-block alignment — both superseded by v3.
frame (edge energy lost). Conditions: uncorrected reception (UWCA and
OFDMA), block-wise realignment using pilot-estimated offsets (each
user's block region shifted back individually), and realignment with a
deliberately impaired estimator (+-1 symbol on 20% of users).
- **Key results.** Uncorrected offsets are catastrophic for *every*
embedding-level scheme (one symbol: UWCA 0.26 -> 0.73, OFDMA 0.51 ->
0.76) because i.i.d. embedding coordinates fully decorrelate under a
one-symbol misalignment. With realignment the degradation is gradual
(0.266 -> 0.304 @Delta=1, 0.455 @Delta=8) and realigned UWCA stays below
realigned OFDMA (0.536-0.638) at every offset. The impaired estimator
costs 0.14 SER — whole-symbol residuals sacrifice the affected block, so
timing must be sub-symbol (standard timing advance).
- **Artifacts.** `rev2/e4_v3_async.py` -> `rev2/data/e4_v3_async.json`;
`fig/fig_async.pdf` (repo).
0.76), because i.i.d. embedding coordinates fully decorrelate under a
one-symbol misalignment — synchronization is a shared physical-layer
prerequisite, not a property of the multiple-access mechanism. With
realignment the degradation is gradual (0.266 -> 0.304 @Delta=1, 0.455
@Delta=8) and realigned UWCA stays below realigned OFDMA (0.536-0.638)
at every offset. The impaired estimator costs 0.14 SER: whole-symbol
residuals sacrifice the affected block, so timing must be held to
sub-symbol accuracy (which the closed-loop timing advance provides).
- **Artifacts.** `experiments/e4_async.py` ->
`experiments/data/e4_async.json`; `fig/fig_async.pdf`.
## E5 — Nonlinear inter-user semantic structure
*Reviewer concerns: R1-7, R2-5, R3-2 (linear scalar model too restrictive).*
- **Question.** Does the mechanism survive when no scalar or linear
description of the inter-user dependence exists?
- **Setup.** Embeddings e_u = normalize(g_u([s; p_u])) with fixed random
two-layer tanh view networks g_u per user (seed 7): users share the scene
s only through independent nonlinear transformations. Cases: shared scene
vs independent scenes (control). Schemes: trained UWCA, OFDMA, NOMA-SIC,
and the scalar-parameterized genie LMMSE fed the *measured* mean pairwise
cosine.
- **Key results.** Linear correlation is destroyed (mean cosine 0.006), so
the genie LMMSE collapses onto the ceiling (0.515 vs OFDMA 0.518 @10 dB)
— no scalar/linear receiver can represent the shared structure. UWCA
still attains 0.363 @10 dB / 0.220 @20 dB. The independent-scene control
(UWCA 0.441) isolates the manifold-prior share, so the further reduction
to 0.363 is pure nonlinear cross-user structure.
- **Artifacts.** `rev2/e5_nonlinear.py` -> `rev2/data/e5_nonlinear.json`
(numbers narrated in manuscript Sec. VII-G).
two-layer tanh view networks g_u per user (seed 7): users share the
scene s only through independent nonlinear transformations. Cases:
shared scene vs independent scenes (control). Schemes: trained UWCA,
OFDMA, NOMA-SIC, and the scalar-parameterized genie LMMSE fed the
*measured* mean pairwise cosine.
- **Key results.** The linear correlation is destroyed (mean cosine
0.006), so the genie LMMSE collapses onto the ceiling (0.515 vs OFDMA
0.518 @10 dB) — no scalar or linear receiver can represent the shared
structure. UWCA still attains 0.363 @10 dB / 0.220 @20 dB. The
independent-scene control (UWCA 0.441) isolates the manifold-prior
share, so the further reduction to 0.363 is pure nonlinear cross-user
structure.
- **Artifacts.** `experiments/e5_nonlinear.py` ->
`experiments/data/e5_nonlinear.json`.
## E6 — Residual orthogonality vs content preservation
*Reviewer concerns: R1-4, R2-2, R3-3 (semantic-orthogonality
self-contradiction).*
- **Question.** What exactly decorrelates at the decoder output: the
delivered content, or the errors?
- **Setup.** E1's trained HIGH decoder; per-sample Pearson correlation
across the 64 dimensions, averaged over user pairs and 100x64 samples per
SNR, for: input embeddings, decoded embeddings, decoding residuals
r_u = e_hat_u - e_u; OFDMA decoded correlation as reference.
- **Key results.** Decoded-embedding correlation rises with SNR from 0.31
toward the 0.39 input level (shared content preserved, not stripped);
residual correlation falls 0.23 -> 0.14 (2.8x below input) — the
emergent residual orthogonality. OFDMA's decoded correlation is 0.00 at
every SNR: orthogonal access erases the inter-user semantic structure.
- **Artifacts.** `rev2/e6_residual_orth.py` ->
`rev2/data/e6_residual_orth.json` -> `fig/fig_resorth.pdf` (manuscript
Fig. 4).
across the 64 dimensions, averaged over user pairs and 100x64 samples
per SNR, for: input embeddings, decoded embeddings, and decoding
residuals r_u = e_hat_u - e_u; OFDMA decoded correlation as reference.
- **Key results.** The decoded-embedding correlation rises with SNR from
0.31 toward the 0.39 input level (the shared content is delivered, not
stripped), while the residual correlation falls 0.23 -> 0.14 (2.8x below
the input level) — the emergent residual orthogonality. OFDMA's decoded
correlation is 0.00 at every SNR: orthogonal access erases the
inter-user semantic structure from the delivered embeddings.
- **Artifacts.** `experiments/e6_residual_orth.py` ->
`experiments/data/e6_residual_orth.json` -> `fig/fig_resorth.pdf`
(manuscript Fig. 4).
## E7 — Meta-adaptation beyond the SNR axis (v2)
*Reviewer concerns: R1-3, R3-4, R3-5 (MAML overkill for a 1-D lookup);
R1-9 (eta stability).*
## E7 — Adaptation across fading families
- **Question.** Does the meta-trained initialization cover a
multi-dimensional space of operating conditions that a one-dimensional
per-SNR model bank cannot, and is the sharpness scalar stable?
- **Setup.** Task family = 6 SNRs x {Rayleigh, Rician K=5 dB, K=10 dB} x
phase residual {0,10} deg (36 tasks). Systems: one initialization
meta-trained over the full family (300 epochs, eta and outer-gradient
norm logged every epoch); per-SNR specialist bank trained on
Rayleigh/no-phase (the 1-D lookup, nearest-SNR index). Held-out tests:
Rician K=20 dB + 15 deg (10/18 dB), Nakagami m=3 + 5 deg (a fading law in
no training task), Rayleigh + 20 deg @6 dB, plus an in-distribution
check; zero-shot and after S in {1,5,10,20} inner steps (SGD 0.02, one
norm logged every epoch); a per-SNR specialist bank trained on
Rayleigh/no-phase and indexed by nearest SNR. Held-out tests: Rician
K=20 dB + 15 deg (10/18 dB), Nakagami m=3 + 5 deg (a fading law in no
training task), Rayleigh + 20 deg @6 dB, plus an in-distribution check;
zero-shot and after S in {1,5,10,20} inner steps (SGD 0.02, one
64-sample support batch).
- **Key results.** Family initialization transfers zero-shot with 9-46%
lower SER than the lookup on every held-out condition (e.g. 0.114 vs
0.158 Rician-K20-15deg @10 dB; 0.139 vs 0.190 unseen Nakagami); inner-loop
adaptation is stable (within 0.005 over 20 steps). eta converges to 0.94
(max 1.00), outer gradient norm <= 0.024 vs clip bound 5 — no softmax
saturation or divergence.
- **Artifacts.** `rev2/e7_v2_meta.py` -> `rev2/data/e7_v2_meta.json`
(includes eta/gradient trajectories).
- **Key results.** The family initialization transfers zero-shot with
9-46% lower SER than the specialist bank on every held-out condition
(e.g. 0.114 vs 0.158 Rician-K20-15deg @10 dB; 0.139 vs 0.190 under the
unseen Nakagami law); inner-loop adaptation is stable (within 0.005 over
20 steps). eta converges to 0.94 (max 1.00) and the outer gradient norm
stays below 0.024 against a clipping bound of 5 — no softmax saturation
or divergence.
- **Artifacts.** `experiments/e7_meta.py` ->
`experiments/data/e7_meta.json` (includes eta/gradient trajectories).
## E8 — End-to-end training with anti-collapse (v2)
*Reviewer concern: R1-5 (representation collapse prevents joint JSCC).*
## E8 — End-to-end training with anti-collapse regularization
- **Question.** Can the encoder be trained jointly with the decoder
without representation collapse, and what causes the collapse when it
occurs?
- **Setup.** Four configurations on HIGH: frozen encoder (reference);
naive end-to-end with the distortion measured against the encoder's own
output (the collapse-prone moving target); source-anchored end-to-end
(distortion vs normalize(x)); moving target + VICReg-style
variance-covariance regularizer (hinge at 1/sqrt(d) per-dim std, 25x
variance + 100/d covariance weights). Collapse-proof metric: batch
nearest-neighbor retrieval accuracy over the *encoded* gallery, plus the
effective rank of the encoder-output covariance.
- **Key results.** Naive collapses exactly as the reviewers expect:
retrieval falls to chance (0.005 vs 0.536 frozen @10 dB) the pathology
is the moving-target objective, not the cross-attention. Source anchoring
keeps effective rank 59.6/64 and lands within 0.04 SER of frozen; VICReg
restores retrieval to 0.509 with a learned task-specific code. E2E
training is therefore demonstrated, and the frozen encoder in the main
experiments is a controlled-isolation choice.
- **Artifacts.** `rev2/e8_v2_e2e.py` -> `rev2/data/e8_v2_e2e.json`.
output (a moving target); source-anchored end-to-end (distortion vs
normalize(x)); moving target + a VICReg-style variance-covariance
regularizer (hinge at 1/sqrt(d) per-dim std, 25x variance + 100/d
covariance weights). Collapse-proof metric: batch nearest-neighbor
retrieval accuracy over the *encoded* gallery, plus the effective rank
of the encoder-output covariance.
- **Key results.** The naive configuration collapses (retrieval falls to
chance, 0.005 vs 0.536 frozen @10 dB), locating the pathology in the
moving-target objective rather than the cross-attention. Source
anchoring keeps effective rank 59.6 of 64 and lands within 0.04 SER of
the frozen reference; the variance-covariance regularizer restores
retrieval to 0.509 with a learned task-specific code. The frozen encoder
in the main experiments is therefore a controlled-isolation choice.
- **Artifacts.** `experiments/e8_e2e.py` ->
`experiments/data/e8_e2e.json`.
## E9 — Online top-k relevance acquisition and measured overhead
*Reviewer concerns: R1-2, R3-6 (circular dependency), R2-4 (cost at large
populations).*
- **Question.** How does sparse top-k attention obtain the relevance
ranking it needs, starting from no knowledge, and what does the
acquisition cost?
- **Setup.** U=32, eight relevance clusters of four, k=4, briefly trained
full-attention model. Protocol: frames 1-3 full attention (needs no
relevance) while the BS accumulates beta_hat by EWMA (zeta=0.5) of
decoded-embedding cosines; from frame 4, per-row top-k via argpartition
on beta_hat. References: oracle top-k (peers selected from the true
clusters) and permanent full attention. Timing: decoder forward vs
EWMA-update + selection wall-clock for U in {8,16,32,64,128}
(256-sample frames, GPU, 20-run averages).
full-attention model. Protocol: frames 1-3 run full attention (which
needs no relevance knowledge) while the BS accumulates beta_hat by EWMA
(zeta=0.5) of decoded-embedding cosines; from frame 4, per-row top-k via
argpartition on beta_hat. References: an oracle whose peers are selected
from the true clusters, and permanent full attention. Timing: decoder
forward vs EWMA-update-plus-selection wall-clock for
U in {8,16,32,64,128} (256-sample frames, GPU, 20-run averages).
- **Key results.** From the first post-warm-up frame the online selection
matches the oracle exactly and slightly exceeds full attention
(discarding irrelevant peers discards their noise); ranking needs far
less accuracy than estimation because intra-cluster (~0.42) and
inter-cluster (~0) cosines are well separated. Estimation + selection
cost 0.02-0.04 ms vs the 0.3-5.4 ms decoder pass, incurred once per
inter-cluster (~0) cosines are well separated. Estimation plus selection
cost 0.02-0.04 ms against the 0.3-5.4 ms decoder pass, incurred once per
relevance coherence interval.
- **Artifacts.** `rev2/e9_topk_online.py` ->
`rev2/data/e9_topk_online.json` (trajectory + timing).
- **Artifacts.** `experiments/e9_topk_online.py` ->
`experiments/data/e9_topk_online.json` (trajectory + timing).
---
## Legacy experiments (retained from the original study)
## Simulation studies (`simulation/`)
- **Three-scenario SER study** (manuscript Fig. 2): analytical simulation
`legacy/semantic_correlation_sim.py` + trained decoder-only overlay
`legacy/maml_semantic.py --decoder_only`; threshold sweep tau in
[0.30,0.50] and mean-cosine table in `legacy/revision_experiments.py`.
- **beta sweep** (monotone gain law): `legacy/revision_betasweep.py`
(figure `fig/fig4_beta_sweep.pdf`, narrated in Sec. VII-C).
- **Attention heatmaps**: `legacy/plot_figures.py`
(figure `fig/fig6_hlm.pdf`, narrated in Sec. VII-C).
- **Hyperparameter ablations** (S, lambda, d, H, K):
`legacy/revision_ablation.py`, `legacy/revision_dsweep.py`,
`legacy/revision_e2e.py` (Sec. VII-D).
`simulation/semantic_correlation_sim.py` with the trained decoder-only
overlay from `simulation/maml_semantic.py --decoder_only`; threshold
sweep tau in [0.30,0.50] and the mean-cosine comparison in
`simulation/revision_experiments.py`.
- **beta sweep** (monotone gain law): `simulation/revision_betasweep.py`
(figure `fig/fig4_beta_sweep.pdf`).
- **Attention heatmaps**: `simulation/plot_figures.py`
(figure `fig/fig6_hlm.pdf`).
- **Hyperparameter studies** (S, lambda, d, H, K):
`simulation/revision_ablation.py`, `simulation/revision_dsweep.py`,
`simulation/revision_e2e.py`.
- **Real-data study** (manuscript Fig. 5): UCI optical-recognition digits
(8x8=64 dims), `legacy/revision_realdata_train.py` +
`legacy/revision_realdata_plot.py` (Sec. VII-G).
## Reviewer-concern -> experiment map
| Concern | Experiment(s) | Manuscript |
|---|---|---|
| R1-1 phase IUI | E2 | Sec. III-A, VII-E |
| R1-2 / R3-6 top-k circularity | E9 | Sec. V-E, VII-H |
| R1-3 / R3-4 / R3-5 MAML vs lookup | E7 | Sec. V-E, VII-F |
| R1-4 / R2-2 / R3-3 orthogonality contradiction | E6 | Def. 3, Prop. 4, Fig. 4 |
| R1-5 representation collapse | E8 | Sec. VI-B |
| R1-6 dynamic U | E3 | Sec. IV-B, VII-E |
| R1-7 / R2-5 / R3-2 linear scalar model | E5 (+ real data) | Sec. III-B, VII-G |
| R1-8 asynchrony | E4 | Sec. III-A, VII-E |
| R1-9 eta stability | E7 logs | Sec. IV-A, VII-F |
| R1-10 / R3-7 d/U fairness | E1 | Prop. 3, Fig. 3, VII-B |
| R2-1 idealized proofs | assumption block + E1/E5/real data | Sec. V |
| R2-3 sync / CSI / identities / encoder | E4 / E2 / E3 / E8+E5 | Sec. III, VI-B, VII-E |
| R2-4 large-population cost | E9 timing | Sec. VII-H |
| R3-1 DSC novelty | (positioning) | Sec. II-A |
(8x8=64 dimensions), `simulation/revision_realdata_train.py` +
`simulation/revision_realdata_plot.py`.
@@ -1,4 +1,4 @@
"""E1 — Degrees-of-freedom fairness (R1.10, R3.7).
"""E1 — Degrees-of-freedom fairness study.
Adds full-dimensional receivers on the SAME received signal:
- lmmse_blind : optimal linear receiver with cross-user correlation set to 0
@@ -1,5 +1,5 @@
"""E2 — Full complex-baseband phase-error model with inter-user leakage (R1.1)
plus CSI amplitude-error robustness (R2.3).
"""E2 — Full complex-baseband phase-error model with inter-user leakage,
plus CSI amplitude-error robustness.
Three evaluation models on HIGH:
scalar : real channel, per-user cos(dphi) attenuation only (old model)
@@ -1,4 +1,4 @@
"""E3 — Dynamic user arrivals/departures (R1.6, R2.3).
"""E3 — Dynamic user arrivals and departures.
U_max = 8 mask slots, d = 64. Activity-aware meta-training samples a random
active subset each batch; at inference the attention softmax is restricted to
@@ -1,4 +1,4 @@
"""E4 v3 — Timing offsets with BLOCK-WISE receiver realignment (R1.8).
"""E4 — Symbol-timing offsets with block-wise receiver realignment.
The BS knows the per-user timing estimates (pilot-based) and realigns each
user's block region individually inside the single received frame:
@@ -88,7 +88,7 @@ for label, mode, ep in [("uwca_uncorrected", "uwca_unc", 0.0),
for snr in out["snr_eval"]:
cur[str(snr)] = [evaluate(mode, dm, snr, ep) for dm in dgrid]
out["curves"][label] = cur
print(f"[E4v3] {label} 10dB SER: "
print(f"[E4] {label} 10dB SER: "
f"{[round(a[0],3) for a in cur['10.0']]}", flush=True)
save_json("e4_v3_async.json", out)
save_json("e4_async.json", out)
@@ -1,4 +1,4 @@
"""E5 — Nonlinear inter-user semantic structure (R1.7, R2.5, R3.2).
"""E5 — Nonlinear inter-user semantic structure.
Embeddings are produced by fixed random per-user nonlinear view networks
e_u = normalize(g_u([kappa*s ; p_u])), so the inter-user dependence is
@@ -1,4 +1,4 @@
"""E6 — Residual (error) orthogonality vs. content preservation (R1.4, R3.3, R2.2).
"""E6 — Residual (error) orthogonality vs. content preservation.
Resolves the claimed contradiction: the decoded embeddings PRESERVE the shared
scene correlation (rho(e_hat_u, e_hat_v) tracks beta_uv), while the decoding
@@ -1,5 +1,5 @@
"""E7 v2 — Meta-training over the multi-dimensional task family, OOD transfer,
adaptation sweep, and eta/gradient logging.
"""E7 — Meta-training over a multi-dimensional task family: held-out
transfer, adaptation sweep, and eta/gradient logging.
meta : the paper's first-order meta-training aggregated over the FULL
36-task family (SNR x {Rayleigh, Rician K=5,10 dB} x phase {0,10 deg})
@@ -35,15 +35,15 @@ family = [{"snr_db": s, "phase_sigma_deg": p, **f}
eta_log = []
m_meta = UWCA(d, U, H).to(DEVICE)
train_multitask(m_meta, gen, family, epochs=300, tag="E7v2-meta",
train_multitask(m_meta, gen, family, epochs=300, tag="E7-meta",
log_state=eta_log)
torch.save(m_meta.state_dict(), lib.DATA / "e7v2_meta.pt")
torch.save(m_meta.state_dict(), lib.DATA / "e7_meta.pt")
specialists = {}
for s in snrs:
m = UWCA(d, U, H).to(DEVICE)
train_multitask(m, gen, [{"snr_db": s}], epochs=150,
tag=f"E7v2-spec{int(s)}")
tag=f"E7-spec{int(s)}")
specialists[s] = m
@@ -78,7 +78,7 @@ for name, t in test_tasks.items():
sers.append({"ser": s, "cos": c})
row[label] = sers
out["results"][name] = row
print(f"[E7v2] {name}: meta={[round(x['ser'],3) for x in row['meta']]} "
print(f"[E7] {name}: meta={[round(x['ser'],3) for x in row['meta']]} "
f"lookup={[round(x['ser'],3) for x in row['lookup']]}", flush=True)
etas = [e["eta"] for e in eta_log]
@@ -87,7 +87,7 @@ out["eta_traj"] = etas[::5]
out["gnorm_traj"] = gns[::5]
out["eta_final"], out["eta_max"] = etas[-1], max(etas)
out["gnorm_max"] = max(gns)
print(f"[E7v2] eta final={etas[-1]:.3f} max={max(etas):.3f} "
print(f"[E7] eta final={etas[-1]:.3f} max={max(etas):.3f} "
f"gnorm max={max(gns):.3f}", flush=True)
save_json("e7_v2_meta.json", out)
save_json("e7_meta.json", out)
+4 -4
View File
@@ -1,4 +1,4 @@
"""E8 v2 — End-to-end joint encoder-decoder training (R1.5), source-anchored.
"""E8 — End-to-end joint encoder-decoder training, source-anchored metrics.
All fidelity metrics are measured against the SOURCE embedding normalize(x),
never against the trainable encoder output (a moving target that makes
@@ -69,7 +69,7 @@ def train(mode, epochs=300):
torch.nn.utils.clip_grad_norm_(model.parameters(), 5.0)
opt.step()
if ep % 75 == 0:
print(f" [E8v2-{mode}] ep {ep}/{epochs} "
print(f" [E8-{mode}] ep {ep}/{epochs} "
f"loss={float(loss)/len(snrs):.4f}", flush=True)
return model, enc
@@ -119,7 +119,7 @@ for mode in ["frozen", "e2e_moving", "e2e_anchored", "e2e_vicreg"]:
erank = collapse_metrics(enc)
ss, cc, rr = curves(model, enc)
out["configs"][mode] = {"ser": ss, "cos": cc, "retr": rr, "erank": erank}
print(f"[E8v2] {mode}: erank={erank:.1f} SER@10={ss[5]:.3f} "
print(f"[E8] {mode}: erank={erank:.1f} SER@10={ss[5]:.3f} "
f"cos@10={cc[5]:.3f} retr@10={rr[5]:.3f}", flush=True)
save_json("e8_v2_e2e.json", out)
save_json("e8_e2e.json", out)
@@ -1,5 +1,5 @@
"""E9 — Online relevance acquisition for sparse top-k attention (R1.2, R3.6)
and measured selection/sorting overhead (R2.4).
"""E9 — Online relevance acquisition for sparse top-k attention and measured
selection overhead.
Protocol (U=32, 8 clusters of 4, k=4):
frames 1..3 : full attention; the BS estimates beta_hat from the decoded
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Shared library for TWC revision-2 experiments (new submission).
Shared library for the UWCA semantic multiple access studies.
Single-signal uplink model matching the manuscript:
y = sum_v g_v (e_v m_v) + n, g_v = |h_v| e^{jΔφ_v}
All experiments import from here. Seed fixed = 42.
+158 -158
View File
@@ -1,158 +1,158 @@
"""Generate the five new revision figures from data/*.json into ../Relevance_TWCOM_R2/fig/.
Uniform geometry: 8:6 axes box, shared rcParams, no tight bounding box.
"""
import json
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
HERE = Path(__file__).resolve().parent
DATA = HERE / "data"
FIG = HERE.parent / "Relevance_TWCOM_R2" / "fig"
FIG.mkdir(exist_ok=True)
plt.rcParams.update({
"font.size": 9, "axes.labelsize": 10, "axes.titlesize": 10,
"legend.fontsize": 7.5, "xtick.labelsize": 8.5, "ytick.labelsize": 8.5,
"lines.linewidth": 1.4, "lines.markersize": 4.5,
"figure.dpi": 200, "savefig.dpi": 300,
"grid.alpha": 0.35, "axes.grid": True,
})
AXRECT = [0.17, 0.165, 0.79, 0.80] # single panel 8:6-ish
FSIZE = (3.5, 2.75)
def newfig():
f = plt.figure(figsize=FSIZE)
ax = f.add_axes(AXRECT)
return f, ax
def save(f, name, axes=None):
f.canvas.draw()
if axes:
for ax in axes:
for lbl in [ax.xaxis.label, ax.yaxis.label]:
bb = lbl.get_window_extent()
fw, fh = f.canvas.get_width_height()
assert bb.x0 >= -1 and bb.y0 >= -1 and bb.x1 <= fw + 1 \
and bb.y1 <= fh + 1, f"label clipped in {name}"
f.savefig(FIG / name)
plt.close(f)
print("saved", FIG / name)
C = {"ofdma": "#546E7A", "blind": "#8D6E63", "noma": "#E65100",
"uwca": "#1565C0", "genie": "#2E7D32", "extra": "#C62828",
"aux": "#6A1B9A"}
# ---------------------------------------------------------------- fig_fair --
# axes box kept at 2.59 x 2.12 in; panel tags "(a)"/"(b)" BELOW the panels
d = json.load(open(DATA / "e1_fair_baselines.json"))
snr = d["snr"]
FH = 3.10 # taller canvas for below-axis tags
AXH = 2.12 / FH
AXB = 0.86 / FH
f = plt.figure(figsize=(7.1, FH))
axs = [f.add_axes([0.115, AXB, 0.365, AXH]),
f.add_axes([0.615, AXB, 0.365, AXH])]
for ax, sc, ttl in zip(axs, ["HIGH", "MIX"], ["(a) HIGH", "(b) MIX"]):
v = d["scenarios"][sc]
ax.semilogy(snr, v["ofdma"]["ser"], "s--", color=C["ofdma"], label="OFDMA")
ax.semilogy(snr, v["lmmse_blind"]["ser"], "v-", color=C["blind"],
markevery=(1, 2), label="LMMSE-blind")
ax.semilogy(snr, v["noma"]["ser"], "^-.", color=C["noma"], label="NOMA-SIC")
ax.semilogy(snr, v["uwca"]["ser"], "o-", color=C["uwca"], label="UWCA (prop.)")
ax.semilogy(snr, v["lmmse_genie"]["ser"], "d:", color=C["genie"],
label="LMMSE-genie")
ax.set_xlabel("SNR (dB)")
ax.set_ylabel("SER")
ax.set_xlim(0, 20)
ax.text(0.5, -0.31, ttl, transform=ax.transAxes,
ha="center", va="top", fontsize=10)
axs[1].legend(loc="lower left", framealpha=0.9, fontsize=7)
save(f, "fig_fair.pdf", axs)
# -------------------------------------------------------------- fig_resorth --
d = json.load(open(DATA / "e6_residual_orth.json"))
snr = d["snr"]
f, ax = newfig()
ax.plot(snr, d["uwca"]["rho_input"], "k--", label=r"input $\rho(\mathbf{e}_u,\mathbf{e}_v)$")
ax.plot(snr, d["uwca"]["rho_decoded"], "o-", color=C["uwca"],
label=r"UWCA decoded $\rho(\hat{\mathbf{e}}_u,\hat{\mathbf{e}}_v)$")
ax.plot(snr, d["uwca"]["rho_residual"], "s-", color=C["extra"],
label=r"UWCA residual $\rho(\mathbf{r}_u,\mathbf{r}_v)$")
ax.plot(snr, d["ofdma"]["rho_decoded"], "^:", color=C["ofdma"],
label=r"OFDMA decoded")
ax.set_xlabel("SNR (dB)")
ax.set_ylabel("Pearson correlation")
ax.set_xlim(0, 20)
ax.set_ylim(-0.05, 0.62)
ax.legend(loc="upper right", framealpha=0.9, fontsize=6.8)
save(f, "fig_resorth.pdf", [ax])
# --------------------------------------------------------------- fig_phase2 --
d = json.load(open(DATA / "e2_phase_iui.json"))
sg = d["sigma_phi_deg"]
f, ax = newfig()
sty = {"complexI_zerotrain": ("o-", C["uwca"], "mismatch-trained"),
"complexI_augtrain": ("s--", C["genie"], "phase-augmented"),
"complexIQ_iqtrain": ("^:", C["extra"], "two-rail (I/Q)")}
for key, (mk, col, lab) in sty.items():
ax.plot(sg, d["curves"][key]["10.0"]["ser"], mk, color=col, label=lab)
for key, (mk, col, lab) in sty.items():
ax.plot(sg, d["curves"][key]["20.0"]["ser"], mk, color=col, alpha=0.45,
label="_nolegend_")
ax.annotate("10 dB", xy=(1.5, 0.29), fontsize=8)
ax.annotate("20 dB", xy=(1.5, 0.135), fontsize=8)
ax.set_xlabel(r"phase residual $\sigma_\varphi$ (deg)")
ax.set_ylabel("SER")
ax.set_ylim(0.0, 0.45)
ax.legend(loc="upper left", framealpha=0.9, fontsize=7)
save(f, "fig_phase2.pdf", [ax])
# ---------------------------------------------------------------- fig_async --
d = json.load(open(DATA / "e4_v3_async.json"))
dm = d["dmax"]
f, ax = newfig()
cur = d["curves"]
ax.plot(dm, [a[0] for a in cur["uwca_uncorrected"]["10.0"]], "o--",
color=C["uwca"], alpha=0.5, label="UWCA, uncorrected")
ax.plot(dm, [a[0] for a in cur["ofdma_uncorrected"]["10.0"]], "s--",
color=C["ofdma"], alpha=0.5, label="OFDMA, uncorrected")
ax.plot(dm, [a[0] for a in cur["uwca_corrected"]["10.0"]], "o-",
color=C["uwca"], label="UWCA, realigned")
ax.plot(dm, [a[0] for a in cur["ofdma_corrected"]["10.0"]], "s-",
color=C["ofdma"], label="OFDMA, realigned")
ax.plot(dm, [a[0] for a in cur["uwca_corrected_err20"]["10.0"]], "^-.",
color=C["extra"], label="UWCA, realigned (20% est. err.)")
ax.set_xlabel(r"maximum timing offset $\Delta$ (symbols)")
ax.set_ylabel("SER")
ax.set_ylim(0, 1.05)
ax.legend(loc="lower right", ncol=2, framealpha=0.9, fontsize=6.0)
save(f, "fig_async.pdf", [ax])
# ------------------------------------------------------------- fig_dynusers --
d = json.load(open(DATA / "e3_dynamic_users.json"))
ks = d["k"]
f = plt.figure(figsize=FSIZE)
ax = f.add_axes([0.20, 0.165, 0.76, 0.80])
ax.plot(ks, [a[1] for a in d["fixed8"]["10.0"]], "o-", color=C["uwca"],
label="single model, 10 dB")
ax.plot(ks, [a[1] for a in d["fixed8"]["20.0"]], "o--", color=C["uwca"],
alpha=0.5, label="single model, 20 dB")
ok = sorted(int(k) for k in d["oracle"])
ax.plot(ok, [d["oracle"][str(k)]["10.0"][1] for k in ok], "s", ls="none",
color=C["extra"], label="per-count retrained, 10 dB")
ax.plot(ok, [d["oracle"][str(k)]["20.0"][1] for k in ok], "s", ls="none",
mfc="none", color=C["extra"], label="per-count retrained, 20 dB")
ax.set_xlabel(r"number of active users $|\mathcal{A}|$")
ax.set_ylabel(r"mean cosine $\bar{c}$")
ax.set_ylim(0.28, 0.47)
ax.legend(loc="upper left", framealpha=0.9, fontsize=6.8)
save(f, "fig_dynusers.pdf", [ax])
print("ALL FIGURES DONE")
"""Regenerate the figure PDFs from data/*.json into ../fig/.
Uniform geometry: 8:6 axes box, shared rcParams, no tight bounding box.
"""
import json
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
HERE = Path(__file__).resolve().parent
DATA = HERE / "data"
FIG = HERE.parent / "fig"
FIG.mkdir(exist_ok=True)
plt.rcParams.update({
"font.size": 9, "axes.labelsize": 10, "axes.titlesize": 10,
"legend.fontsize": 7.5, "xtick.labelsize": 8.5, "ytick.labelsize": 8.5,
"lines.linewidth": 1.4, "lines.markersize": 4.5,
"figure.dpi": 200, "savefig.dpi": 300,
"grid.alpha": 0.35, "axes.grid": True,
})
AXRECT = [0.17, 0.165, 0.79, 0.80] # single panel 8:6-ish
FSIZE = (3.5, 2.75)
def newfig():
f = plt.figure(figsize=FSIZE)
ax = f.add_axes(AXRECT)
return f, ax
def save(f, name, axes=None):
f.canvas.draw()
if axes:
for ax in axes:
for lbl in [ax.xaxis.label, ax.yaxis.label]:
bb = lbl.get_window_extent()
fw, fh = f.canvas.get_width_height()
assert bb.x0 >= -1 and bb.y0 >= -1 and bb.x1 <= fw + 1 \
and bb.y1 <= fh + 1, f"label clipped in {name}"
f.savefig(FIG / name)
plt.close(f)
print("saved", FIG / name)
C = {"ofdma": "#546E7A", "blind": "#8D6E63", "noma": "#E65100",
"uwca": "#1565C0", "genie": "#2E7D32", "extra": "#C62828",
"aux": "#6A1B9A"}
# ---------------------------------------------------------------- fig_fair --
# axes box kept at 2.59 x 2.12 in; panel tags "(a)"/"(b)" BELOW the panels
d = json.load(open(DATA / "e1_fair_baselines.json"))
snr = d["snr"]
FH = 3.10 # taller canvas for below-axis tags
AXH = 2.12 / FH
AXB = 0.86 / FH
f = plt.figure(figsize=(7.1, FH))
axs = [f.add_axes([0.115, AXB, 0.365, AXH]),
f.add_axes([0.615, AXB, 0.365, AXH])]
for ax, sc, ttl in zip(axs, ["HIGH", "MIX"], ["(a) HIGH", "(b) MIX"]):
v = d["scenarios"][sc]
ax.semilogy(snr, v["ofdma"]["ser"], "s--", color=C["ofdma"], label="OFDMA")
ax.semilogy(snr, v["lmmse_blind"]["ser"], "v-", color=C["blind"],
markevery=(1, 2), label="LMMSE-blind")
ax.semilogy(snr, v["noma"]["ser"], "^-.", color=C["noma"], label="NOMA-SIC")
ax.semilogy(snr, v["uwca"]["ser"], "o-", color=C["uwca"], label="UWCA (prop.)")
ax.semilogy(snr, v["lmmse_genie"]["ser"], "d:", color=C["genie"],
label="LMMSE-genie")
ax.set_xlabel("SNR (dB)")
ax.set_ylabel("SER")
ax.set_xlim(0, 20)
ax.text(0.5, -0.31, ttl, transform=ax.transAxes,
ha="center", va="top", fontsize=10)
axs[1].legend(loc="lower left", framealpha=0.9, fontsize=7)
save(f, "fig_fair.pdf", axs)
# -------------------------------------------------------------- fig_resorth --
d = json.load(open(DATA / "e6_residual_orth.json"))
snr = d["snr"]
f, ax = newfig()
ax.plot(snr, d["uwca"]["rho_input"], "k--", label=r"input $\rho(\mathbf{e}_u,\mathbf{e}_v)$")
ax.plot(snr, d["uwca"]["rho_decoded"], "o-", color=C["uwca"],
label=r"UWCA decoded $\rho(\hat{\mathbf{e}}_u,\hat{\mathbf{e}}_v)$")
ax.plot(snr, d["uwca"]["rho_residual"], "s-", color=C["extra"],
label=r"UWCA residual $\rho(\mathbf{r}_u,\mathbf{r}_v)$")
ax.plot(snr, d["ofdma"]["rho_decoded"], "^:", color=C["ofdma"],
label=r"OFDMA decoded")
ax.set_xlabel("SNR (dB)")
ax.set_ylabel("Pearson correlation")
ax.set_xlim(0, 20)
ax.set_ylim(-0.05, 0.62)
ax.legend(loc="upper right", framealpha=0.9, fontsize=6.8)
save(f, "fig_resorth.pdf", [ax])
# --------------------------------------------------------------- fig_phase2 --
d = json.load(open(DATA / "e2_phase_iui.json"))
sg = d["sigma_phi_deg"]
f, ax = newfig()
sty = {"complexI_zerotrain": ("o-", C["uwca"], "mismatch-trained"),
"complexI_augtrain": ("s--", C["genie"], "phase-augmented"),
"complexIQ_iqtrain": ("^:", C["extra"], "two-rail (I/Q)")}
for key, (mk, col, lab) in sty.items():
ax.plot(sg, d["curves"][key]["10.0"]["ser"], mk, color=col, label=lab)
for key, (mk, col, lab) in sty.items():
ax.plot(sg, d["curves"][key]["20.0"]["ser"], mk, color=col, alpha=0.45,
label="_nolegend_")
ax.annotate("10 dB", xy=(1.5, 0.29), fontsize=8)
ax.annotate("20 dB", xy=(1.5, 0.135), fontsize=8)
ax.set_xlabel(r"phase residual $\sigma_\varphi$ (deg)")
ax.set_ylabel("SER")
ax.set_ylim(0.0, 0.45)
ax.legend(loc="upper left", framealpha=0.9, fontsize=7)
save(f, "fig_phase2.pdf", [ax])
# ---------------------------------------------------------------- fig_async --
d = json.load(open(DATA / "e4_async.json"))
dm = d["dmax"]
f, ax = newfig()
cur = d["curves"]
ax.plot(dm, [a[0] for a in cur["uwca_uncorrected"]["10.0"]], "o--",
color=C["uwca"], alpha=0.5, label="UWCA, uncorrected")
ax.plot(dm, [a[0] for a in cur["ofdma_uncorrected"]["10.0"]], "s--",
color=C["ofdma"], alpha=0.5, label="OFDMA, uncorrected")
ax.plot(dm, [a[0] for a in cur["uwca_corrected"]["10.0"]], "o-",
color=C["uwca"], label="UWCA, realigned")
ax.plot(dm, [a[0] for a in cur["ofdma_corrected"]["10.0"]], "s-",
color=C["ofdma"], label="OFDMA, realigned")
ax.plot(dm, [a[0] for a in cur["uwca_corrected_err20"]["10.0"]], "^-.",
color=C["extra"], label="UWCA, realigned (20% est. err.)")
ax.set_xlabel(r"maximum timing offset $\Delta$ (symbols)")
ax.set_ylabel("SER")
ax.set_ylim(0, 1.05)
ax.legend(loc="lower right", ncol=2, framealpha=0.9, fontsize=6.0)
save(f, "fig_async.pdf", [ax])
# ------------------------------------------------------------- fig_dynusers --
d = json.load(open(DATA / "e3_dynamic_users.json"))
ks = d["k"]
f = plt.figure(figsize=FSIZE)
ax = f.add_axes([0.20, 0.165, 0.76, 0.80])
ax.plot(ks, [a[1] for a in d["fixed8"]["10.0"]], "o-", color=C["uwca"],
label="single model, 10 dB")
ax.plot(ks, [a[1] for a in d["fixed8"]["20.0"]], "o--", color=C["uwca"],
alpha=0.5, label="single model, 20 dB")
ok = sorted(int(k) for k in d["oracle"])
ax.plot(ok, [d["oracle"][str(k)]["10.0"][1] for k in ok], "s", ls="none",
color=C["extra"], label="per-count retrained, 10 dB")
ax.plot(ok, [d["oracle"][str(k)]["20.0"][1] for k in ok], "s", ls="none",
mfc="none", color=C["extra"], label="per-count retrained, 20 dB")
ax.set_xlabel(r"number of active users $|\mathcal{A}|$")
ax.set_ylabel(r"mean cosine $\bar{c}$")
ax.set_ylim(0.28, 0.47)
ax.legend(loc="upper left", framealpha=0.9, fontsize=6.8)
save(f, "fig_dynusers.pdf", [ax])
print("ALL FIGURES DONE")
-151
View File
@@ -1,151 +0,0 @@
{
"dmax": [
0,
1,
2,
4,
8
],
"snr_eval": [
10.0,
20.0
],
"sync_trained": {
"10.0": [
[
0.2683203125,
0.49533248856663703
],
[
0.7208984375,
0.242251892760396
],
[
0.83734375,
0.15587599329650403
],
[
0.91517578125,
0.09062198633328081
],
[
0.95876953125,
0.0465814154734835
]
],
"20.0": [
[
0.10884765625,
0.5421956545114517
],
[
0.65294921875,
0.2639770006388426
],
[
0.79462890625,
0.1728138119354844
],
[
0.8930859375,
0.09787379436194897
],
[
0.94958984375,
0.048505370183847846
]
]
},
"aug_trained": {
"10.0": [
[
0.61974609375,
0.4028245759010315
],
[
0.80044921875,
0.20371038138866424
],
[
0.8644921875,
0.14209178265184164
],
[
0.91814453125,
0.08969146355986596
],
[
0.95642578125,
0.05853485576808452
]
],
"20.0": [
[
0.50931640625,
0.4357883331179619
],
[
0.7403515625,
0.22275549590587615
],
[
0.82765625,
0.15037441711872815
],
[
0.8998046875,
0.09680132243782281
],
[
0.94701171875,
0.06260592238977551
]
]
},
"ofdma": {
"10.0": [
[
0.508203125,
0.4361314806342125
],
[
0.76318359375,
0.21654599383473397
],
[
0.84802734375,
0.14174282837659122
],
[
0.91091796875,
0.08382582331076265
],
[
0.9557421875,
0.043560955775901675
]
],
"20.0": [
[
0.32919921875,
0.48276128739118573
],
[
0.68083984375,
0.23667482212185859
],
[
0.79951171875,
0.15432738859206438
],
[
0.88361328125,
0.09289443053305149
],
[
0.94376953125,
0.04812081384472549
]
]
}
}
-245
View File
@@ -1,245 +0,0 @@
{
"dmax": [
0,
1,
2,
4,
8
],
"snr_eval": [
10.0,
20.0
],
"curves": {
"uwca_uncorrected": {
"10.0": [
[
0.26302734375,
0.49663727134466173
],
[
0.72521484375,
0.24080994725227356
],
[
0.83720703125,
0.1565597005933523
],
[
0.9140234375,
0.09298811599612236
],
[
0.95828125,
0.045774847799912095
]
],
"20.0": [
[
0.1059765625,
0.5424016201496125
],
[
0.64892578125,
0.2658117674291134
],
[
0.79361328125,
0.1722280565276742
],
[
0.89083984375,
0.09743562746793032
],
[
0.94720703125,
0.051459523779340086
]
]
},
"ofdma_uncorrected": {
"10.0": [
[
0.50865234375,
0.43710263311862946
],
[
0.76029296875,
0.2157912875711918
],
[
0.8459375,
0.14081338860094547
],
[
0.9133984375,
0.08355670671910048
],
[
0.9558984375,
0.04483862698078155
]
],
"20.0": [
[
0.3259765625,
0.4835157571732998
],
[
0.68376953125,
0.23547276966273784
],
[
0.7949609375,
0.1552288055792451
],
[
0.886640625,
0.09062881361693144
],
[
0.94580078125,
0.04779002937488258
]
]
},
"uwca_corrected": {
"10.0": [
[
0.2655859375,
0.49615009009838107
],
[
0.44373046875,
0.4498268289864063
],
[
0.5089453125,
0.4324074760079384
],
[
0.578828125,
0.4135142582654953
],
[
0.6497265625,
0.38951330006122586
]
],
"20.0": [
[
0.11287109375,
0.5400555384159088
],
[
0.30025390625,
0.4892691922187805
],
[
0.38263671875,
0.46662179097533224
],
[
0.4680078125,
0.4436407870054245
],
[
0.56431640625,
0.41600462675094607
]
]
},
"ofdma_corrected": {
"10.0": [
[
0.50662109375,
0.4369038107991219
],
[
0.53580078125,
0.42834869906306267
],
[
0.55255859375,
0.42323953911662104
],
[
0.58455078125,
0.41399665489792825
],
[
0.638125,
0.3948879507184029
]
],
"20.0": [
[
0.32869140625,
0.48293048948049544
],
[
0.371953125,
0.4720873585343361
],
[
0.403359375,
0.46447199031710623
],
[
0.45724609375,
0.44948955610394475
],
[
0.53859375,
0.42527498200535774
]
]
},
"uwca_corrected_err20": {
"10.0": [
[
0.26931640625,
0.49530661895871164
],
[
0.5301953125,
0.3851976223289967
],
[
0.58978515625,
0.3633556814491749
],
[
0.65712890625,
0.33998452201485635
],
[
0.7178125,
0.3151700422167778
]
],
"20.0": [
[
0.11326171875,
0.5398727428913116
],
[
0.407734375,
0.4189111949503422
],
[
0.48626953125,
0.39070201337337496
],
[
0.56044921875,
0.3684333018958569
],
[
0.64603515625,
0.33623267963528636
]
]
}
}
}
File diff suppressed because it is too large Load Diff
-107
View File
@@ -1,107 +0,0 @@
{
"snr": [
0.0,
2.0,
4.0,
6.0,
8.0,
10.0,
12.0,
14.0,
16.0,
18.0,
20.0
],
"configs": {
"frozen": {
"ser": [
0.7918229166666667,
0.685703125,
0.5696354166666666,
0.45276041666666667,
0.35044270833333335,
0.270703125,
0.2078125,
0.16442708333333333,
0.14369791666666668,
0.11671875,
0.10502604166666667
],
"cos": [
0.33901638666788736,
0.3792428519328435,
0.41583929598331454,
0.44846010764439903,
0.47460420747598014,
0.49550534566243487,
0.5113285048802694,
0.5235649347305298,
0.530758779446284,
0.5387160217761994,
0.5428356532255808
],
"erank": 63.314430236816406,
"rho_off": 0.998196005821228
},
"naive": {
"ser": [
0.00020833333333333335,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"cos": [
0.9946125821272532,
0.9967783908049266,
0.9977629196643829,
0.9983737409114838,
0.9987331410249074,
0.9989773007233937,
0.9991132164001465,
0.9992019935448965,
0.999260938167572,
0.9992937278747559,
0.9993155153592428
],
"erank": 41.47739028930664,
"rho_off": 0.999997615814209
},
"vicreg": {
"ser": [
0.7002864583333334,
0.5877083333333334,
0.45828125,
0.36255208333333333,
0.28338541666666667,
0.21833333333333332,
0.171875,
0.14651041666666667,
0.12901041666666666,
0.12166666666666667,
0.11083333333333334
],
"cos": [
0.3612262072165807,
0.40026772101720176,
0.4415491064389547,
0.4709076561530431,
0.49452649215857186,
0.5151174678405126,
0.5307369756698609,
0.5408004929622015,
0.5477419094244639,
0.5502727842330932,
0.5561861228942871
],
"erank": 34.763675689697266,
"rho_off": 0.9972559809684753
}
}
}