Reproducibility package: UWCA semantic multiple access (TWC submission)
This commit is contained in:
@@ -0,0 +1,60 @@
|
|||||||
|
# UWCA Semantic Multiple Access — Reproducibility Package
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Python 3.10+ with `torch` (CUDA optional; results generated on an NVIDIA
|
||||||
|
RTX A4500, PyTorch 2.10, CUDA 12.8), `numpy`, `matplotlib`, and
|
||||||
|
`scikit-learn` (real-data study only).
|
||||||
|
- 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).
|
||||||
|
- `fig/` — figure PDFs as included in the manuscript.
|
||||||
|
|
||||||
|
## Figure/number → 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/` |
|
||||||
|
|
||||||
|
## 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
|
||||||
|
```
|
||||||
|
|
||||||
|
Each experiment script is self-contained and writes its JSON into
|
||||||
|
`rev2/data/`.
|
||||||
|
|
||||||
|
## Citation and license
|
||||||
|
|
||||||
|
To be completed upon acceptance.
|
||||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
+929
@@ -0,0 +1,929 @@
|
|||||||
|
"""
|
||||||
|
=============================================================================
|
||||||
|
Multi-User Semantic Communication — PyTorch MAML Training + Evaluation
|
||||||
|
IEEE JSAC: Meta-Learned Cross-Attention for Multi-User Semantic
|
||||||
|
Communication over Wireless Fading Channels
|
||||||
|
|
||||||
|
구조
|
||||||
|
----
|
||||||
|
SemanticEncoder : MLP x_u → e_u ∈ ℝ^d
|
||||||
|
UserWiseCrossAttn : user-wise cross-attention decoder Y → ê_u (논문 식 4–6)
|
||||||
|
MAMLTrainer : MAML outer/inner loop over SNR tasks (논문 식 7–9)
|
||||||
|
|
||||||
|
비교 대상
|
||||||
|
----------
|
||||||
|
OFDMA : 대역폭 B/U 분할 (SNR 패널티 −10log10(U))
|
||||||
|
NOMA-SIC : 전력 중첩 + 순차 간섭 제거
|
||||||
|
Joint : 고정 SNR 분포에서 표준 joint training
|
||||||
|
|
||||||
|
지표
|
||||||
|
----
|
||||||
|
SER : cosine-sim(ê_u, e_u) < τ 인 사용자 비율
|
||||||
|
ρ : 디코딩된 임베딩의 사용자 간 Pearson 상관계수
|
||||||
|
|
||||||
|
실행 방법
|
||||||
|
----------
|
||||||
|
python maml_semantic.py # 학습 + 평가
|
||||||
|
python maml_semantic.py --fast # 빠른 디버그 (epoch 축소)
|
||||||
|
python maml_semantic.py --eval_only --ckpt results/models.pt
|
||||||
|
=============================================================================
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import copy
|
||||||
|
import warnings
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
import torch.nn as nn
|
||||||
|
import torch.nn.functional as F
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use("Agg")
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import matplotlib.gridspec as gridspec
|
||||||
|
from matplotlib.colors import LinearSegmentedColormap
|
||||||
|
|
||||||
|
warnings.filterwarnings("ignore")
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 0. CONFIG
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
def get_cfg():
|
||||||
|
p = argparse.ArgumentParser()
|
||||||
|
p.add_argument("--d", type=int, default=64)
|
||||||
|
p.add_argument("--U", type=int, default=4)
|
||||||
|
p.add_argument("--H", type=int, default=4, help="attention heads")
|
||||||
|
p.add_argument("--tau", type=float, default=0.45)
|
||||||
|
p.add_argument("--lam", type=float, default=0.1, help="ortho loss weight λ")
|
||||||
|
p.add_argument("--snr_min", type=float, default=0.0)
|
||||||
|
p.add_argument("--snr_max", type=float, default=20.0)
|
||||||
|
p.add_argument("--snr_step", type=float, default=2.0)
|
||||||
|
p.add_argument("--inner_lr", type=float, default=0.01)
|
||||||
|
p.add_argument("--inner_steps", type=int, default=5)
|
||||||
|
p.add_argument("--outer_lr", type=float, default=1e-3)
|
||||||
|
p.add_argument("--meta_epochs", type=int, default=300)
|
||||||
|
p.add_argument("--joint_epochs",type=int, default=300)
|
||||||
|
p.add_argument("--batch", type=int, default=64)
|
||||||
|
p.add_argument("--n_mc", type=int, default=500)
|
||||||
|
p.add_argument("--seed", type=int, default=42)
|
||||||
|
p.add_argument("--outdir", type=str, default="results")
|
||||||
|
p.add_argument("--eval_only", action="store_true")
|
||||||
|
p.add_argument("--ckpt", type=str, default=None)
|
||||||
|
p.add_argument("--fast", action="store_true",
|
||||||
|
help="빠른 디버그: epoch을 1/10로 축소")
|
||||||
|
p.add_argument("--device", type=str, default="auto")
|
||||||
|
p.add_argument("--scenario", type=str, default="DEFAULT",
|
||||||
|
help="훈련 시나리오: DEFAULT/HIGH/LOW/MIX/HETERO/ASYM/ALL")
|
||||||
|
p.add_argument("--decoder_only", action="store_true",
|
||||||
|
help="Option A: IdentityEncoder(고정) + decoder만 MAML 훈련")
|
||||||
|
args = p.parse_args()
|
||||||
|
if args.fast:
|
||||||
|
args.meta_epochs = max(30, args.meta_epochs // 10)
|
||||||
|
args.joint_epochs = max(30, args.joint_epochs // 10)
|
||||||
|
args.n_mc = max(50, args.n_mc // 10)
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 1. DATA GENERATION (자율주행 합성 임베딩)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# u0: 보행자 bounding-box (scene 결합도 높음)
|
||||||
|
# u1: 신호등 상태 (중간)
|
||||||
|
# u2: 차선 세그먼테이션 (낮음)
|
||||||
|
# u3: 차량 속도/방향 (가장 낮음)
|
||||||
|
BLEND = [0.55, 0.45, 0.30, 0.20]
|
||||||
|
USER_LABELS = ['보행자\n(U1)', '신호등\n(U2)', '차선\n(U3)', '속도\n(U4)']
|
||||||
|
USER_COLORS = ['#1565C0', '#2E7D32', '#C62828', '#6A1B9A']
|
||||||
|
|
||||||
|
# Scenario configs matching semantic_correlation_sim.py
|
||||||
|
SCENARIO_CONFIGS = {
|
||||||
|
'DEFAULT': {'beta_u': [0.55, 0.45, 0.30, 0.20], 'scenes': [0, 0, 0, 0]},
|
||||||
|
'HIGH': {'beta_u': [0.65, 0.65, 0.60, 0.60], 'scenes': [0, 0, 0, 0]},
|
||||||
|
'LOW': {'beta_u': [0.65, 0.05, 0.05, 0.05], 'scenes': [0, 1, 2, 3]},
|
||||||
|
'MIX': {'beta_u': [0.65, 0.65, 0.05, 0.05], 'scenes': [0, 0, 1, 2]},
|
||||||
|
'HETERO': {'beta_u': [0.75, 0.75, 0.45, 0.08], 'scenes': [0, 0, 0, 1]},
|
||||||
|
'ASYM': {'beta_u': [0.72, 0.58, 0.35, 0.12], 'scenes': [0, 0, 0, 0]},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def gen_embeddings(n: int, d: int, U: int, rng,
|
||||||
|
scenario_cfg=None) -> torch.Tensor:
|
||||||
|
"""단위 정규화된 ground-truth 임베딩 (n, U, d)
|
||||||
|
scenario_cfg: dict with 'beta_u' and 'scenes' keys (from SCENARIO_CONFIGS).
|
||||||
|
If None, uses the default BLEND with a single shared scene.
|
||||||
|
"""
|
||||||
|
if scenario_cfg is None:
|
||||||
|
blend = BLEND
|
||||||
|
scenes = [0] * U
|
||||||
|
else:
|
||||||
|
blend = scenario_cfg['beta_u']
|
||||||
|
scenes = scenario_cfg['scenes']
|
||||||
|
|
||||||
|
# One unit-norm scene vector per unique scene key (full D dims, matches sim)
|
||||||
|
unique_scenes = sorted(set(scenes))
|
||||||
|
scene_vecs: dict = {}
|
||||||
|
for sc in unique_scenes:
|
||||||
|
v = rng.standard_normal(d)
|
||||||
|
scene_vecs[sc] = v / (np.linalg.norm(v) + 1e-8)
|
||||||
|
|
||||||
|
embs = []
|
||||||
|
for u in range(U):
|
||||||
|
b = blend[u] if u < len(blend) else 0.15
|
||||||
|
private = rng.standard_normal((n, d))
|
||||||
|
p_hat = private / (np.linalg.norm(private, axis=-1, keepdims=True) + 1e-8)
|
||||||
|
s = scene_vecs[scenes[u]]
|
||||||
|
# Eq. (2): e_u = sqrt(1-β²)·p̂_u + β·s (semantic_correlation_sim.py convention)
|
||||||
|
e = np.sqrt(max(1 - b ** 2, 0)) * p_hat + b * s[None, :]
|
||||||
|
e /= np.linalg.norm(e, axis=-1, keepdims=True) + 1e-8
|
||||||
|
embs.append(e)
|
||||||
|
return torch.from_numpy(np.stack(embs, axis=1)).float() # (n, U, d)
|
||||||
|
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 2. CHANNEL MODELS (SE — Shared Embedding superposition framework)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
def _block_masks(U: int, d: int, device) -> torch.Tensor:
|
||||||
|
"""Hard block masks (U, d): user u owns dims [u·DPU, (u+1)·DPU).
|
||||||
|
Matches semantic_correlation_sim.py MASKS construction exactly."""
|
||||||
|
DPU = d // U
|
||||||
|
masks = torch.zeros(U, d, device=device)
|
||||||
|
for u in range(U):
|
||||||
|
masks[u, u * DPU:(u + 1) * DPU] = 1.0
|
||||||
|
return masks
|
||||||
|
|
||||||
|
|
||||||
|
def se_channel(E: torch.Tensor, snr_db: float) -> torch.Tensor:
|
||||||
|
"""SE superposition channel (논문 Eq. 3).
|
||||||
|
|
||||||
|
x_u = e_u ⊙ m_u (hard block mask; user u owns D/U contiguous dims)
|
||||||
|
y_tx = Σ_u x_u (superimposed D-dim signal)
|
||||||
|
y_rx,u = h_u · y_tx + n_u (independent Rayleigh per user)
|
||||||
|
|
||||||
|
E : (n, U, d) unit-norm embeddings
|
||||||
|
Returns Y_rx : (n, U, d) — row u is user u's received copy of y_tx.
|
||||||
|
|
||||||
|
Cross-attention 구조 설명:
|
||||||
|
R_{u,i} = y_rx,u ⊙ m_i 는 user u의 수신 신호에서 subspace i를 추출.
|
||||||
|
h_u · x_i[dims_i] + noise 만 남으므로, beta_ui > 0이면 e_u 정보를 담고 있음.
|
||||||
|
"""
|
||||||
|
n, U, d = E.shape
|
||||||
|
masks = _block_masks(U, d, E.device) # (U, d)
|
||||||
|
X = E * masks[None, :, :] # (n, U, d)
|
||||||
|
Ytx = X.sum(1) # (n, d) superimposed
|
||||||
|
|
||||||
|
h = (torch.randn(n, U, 1, device=E.device) ** 2 +
|
||||||
|
torch.randn(n, U, 1, device=E.device) ** 2).sqrt() * (0.5 ** 0.5) # (n, U, 1)
|
||||||
|
|
||||||
|
snr_lin = 10 ** (snr_db / 10)
|
||||||
|
noise_std = (Ytx.pow(2).mean() / snr_lin).sqrt()
|
||||||
|
Yrx = h * Ytx[:, None, :] + torch.randn(n, U, d, device=E.device) * noise_std
|
||||||
|
return Yrx # (n, U, d)
|
||||||
|
|
||||||
|
|
||||||
|
def se_ofdma_decode(Y_rx: torch.Tensor) -> torch.Tensor:
|
||||||
|
"""SE-OFDMA decoder: ê_u = normalize(y_rx,u ⊙ m_u).
|
||||||
|
Each user uses only their own D/U-dim subspace block; no SNR penalty.
|
||||||
|
Matches ofdma_se_decoder() in semantic_correlation_sim.py."""
|
||||||
|
n, U, d = Y_rx.shape
|
||||||
|
masks = _block_masks(U, d, Y_rx.device) # (U, d)
|
||||||
|
return F.normalize(Y_rx * masks[None, :, :], dim=-1) # (n, U, d)
|
||||||
|
|
||||||
|
|
||||||
|
def se_noma_decode(E: torch.Tensor, snr_db: float) -> torch.Tensor:
|
||||||
|
"""Full-band power-domain NOMA with SIC (standard NOMA, fair comparison).
|
||||||
|
|
||||||
|
All U users share the SAME full d-dim band — no subspace masking — so the
|
||||||
|
received signal is a single d-dim power-domain superposition:
|
||||||
|
x_u = sqrt(p_u) · e_u (full-band, power-weighted)
|
||||||
|
y = Σ_u h_u · sqrt(p_u) · e_u + n (single d-dim signal at BS)
|
||||||
|
|
||||||
|
This reflects NOMA's defining resource advantage fairly: every user accesses
|
||||||
|
the full d-dim band (vs. OFDMA's exclusive d/U-dim block) at the cost of
|
||||||
|
inter-user interference, while the total transmit power Σ_u p_u = 1 matches
|
||||||
|
the OFDMA budget, keeping the comparison both power- and bandwidth-fair.
|
||||||
|
Each user is reconstructed from the full d-dim signal via successive
|
||||||
|
interference cancellation in descending allocated-power order.
|
||||||
|
|
||||||
|
Matches noma_ul_channel()/noma_sic_decoder() in semantic_correlation_sim.py
|
||||||
|
and noma_ch()/noma_sic() in revision_realdata_plot.py.
|
||||||
|
|
||||||
|
E : (n, U, d) unit-norm ground-truth embeddings.
|
||||||
|
Returns decoded : (n, U, d) per-user estimates over the full d-dim band.
|
||||||
|
"""
|
||||||
|
n, U, d = E.shape
|
||||||
|
pa = torch.tensor([0.40, 0.30, 0.20, 0.10], device=E.device)[:U]
|
||||||
|
pa = pa / pa.sum() # Σ p_u = 1 (power-fair)
|
||||||
|
|
||||||
|
h = (torch.randn(n, U, 1, device=E.device) ** 2 +
|
||||||
|
torch.randn(n, U, 1, device=E.device) ** 2).sqrt() * (0.5 ** 0.5) # (n, U, 1)
|
||||||
|
|
||||||
|
y = (E * pa.sqrt()[None, :, None] * h).sum(1) # (n, d) full-band superposition
|
||||||
|
snr_lin = 10 ** (snr_db / 10)
|
||||||
|
noise_std = (y.pow(2).mean() / snr_lin).sqrt()
|
||||||
|
y = y + torch.randn(n, d, device=E.device) * noise_std # (n, d)
|
||||||
|
|
||||||
|
order = torch.argsort(pa, descending=True) # high-power first
|
||||||
|
res = y.clone() # (n, d) shared residual
|
||||||
|
decoded = torch.zeros(n, U, d, device=E.device) # (n, U, d)
|
||||||
|
for u_idx in order:
|
||||||
|
u = int(u_idx.item())
|
||||||
|
ê_u = F.normalize(res / (h[:, u, :] + 1e-8), dim=-1) # (n, d) over full band
|
||||||
|
decoded[:, u, :] = ê_u
|
||||||
|
res = res - h[:, u, :] * pa[u].sqrt() * ê_u # full-band cancellation
|
||||||
|
|
||||||
|
return decoded # (n, U, d)
|
||||||
|
|
||||||
|
|
||||||
|
def se_sfdma_decode(E: torch.Tensor, snr_db: float) -> torch.Tensor:
|
||||||
|
"""SFDMA — Semantic Feature Division Multiple Access (Ma et al., 2024).
|
||||||
|
|
||||||
|
Each user maps its embedding onto an assigned orthonormal d/U-dim *semantic*
|
||||||
|
subspace; all users transmit simultaneously over the SAME full d-dim band, and
|
||||||
|
the receiver separates them by projecting onto each user's subspace:
|
||||||
|
x_u = P_u e_u (P_u: projector onto user u's semantic subspace)
|
||||||
|
y = Σ_u h_u · x_u + n (full-band superposition)
|
||||||
|
ê_u = normalize(P_u y / h_u)
|
||||||
|
|
||||||
|
Fair vs. NOMA/UWCA: SFDMA shares the full band (not OFDMA's exclusive 1/U
|
||||||
|
physical sub-band) and is given the best case of *perfectly* orthogonal
|
||||||
|
subspaces, so the projections separate users with no inter-user interference.
|
||||||
|
The subspaces are taken as the canonical orthogonal partition (the block
|
||||||
|
basis) so the comparison isolates the multiple-access mechanism rather than an
|
||||||
|
arbitrary basis–data alignment. Because the orthogonality confines each user
|
||||||
|
to d/U effective dimensions — the same subspace ceiling as OFDMA — and discards
|
||||||
|
the inter-user correlation, SFDMA coincides with OFDMA despite using the full
|
||||||
|
band. Matches sfdma() in revision_realdata_plot.py.
|
||||||
|
|
||||||
|
E : (n, U, d) unit-norm embeddings. Returns (n, U, d) estimates.
|
||||||
|
"""
|
||||||
|
n, U, d = E.shape
|
||||||
|
masks = _block_masks(U, d, E.device) # (U, d) orthogonal subspaces
|
||||||
|
|
||||||
|
h = (torch.randn(n, U, 1, device=E.device) ** 2 +
|
||||||
|
torch.randn(n, U, 1, device=E.device) ** 2).sqrt() * (0.5 ** 0.5) # (n, U, 1)
|
||||||
|
|
||||||
|
X = E * masks[None, :, :] # (n, U, d) project onto own subspace
|
||||||
|
y = (h * X).sum(1) # (n, d) full-band superposition
|
||||||
|
snr_lin = 10 ** (snr_db / 10)
|
||||||
|
noise_std = (y.pow(2).mean() / snr_lin).sqrt()
|
||||||
|
y = y + torch.randn(n, d, device=E.device) * noise_std # (n, d)
|
||||||
|
|
||||||
|
return torch.stack(
|
||||||
|
[F.normalize((y * masks[u]) / (h[:, u, :] + 1e-8), dim=-1) for u in range(U)], 1)
|
||||||
|
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 3. NEURAL MODULES
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
class SemanticEncoder(nn.Module):
|
||||||
|
"""f_φ : ℝ^d → ℝ^d (MLP + LayerNorm + 단위 정규화)"""
|
||||||
|
def __init__(self, d: int):
|
||||||
|
super().__init__()
|
||||||
|
self.net = nn.Sequential(
|
||||||
|
nn.Linear(d, d * 2), nn.LayerNorm(d * 2), nn.GELU(),
|
||||||
|
nn.Linear(d * 2, d),
|
||||||
|
)
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
return F.normalize(self.net(x), dim=-1)
|
||||||
|
|
||||||
|
|
||||||
|
class IdentityEncoder(nn.Module):
|
||||||
|
"""No-op encoder: L2-normalizes input only (no learnable params).
|
||||||
|
Used for decoder-only training (Option A) so that the channel model
|
||||||
|
directly transmits the raw embeddings, matching the analytical simulation."""
|
||||||
|
def forward(self, x):
|
||||||
|
return F.normalize(x, dim=-1)
|
||||||
|
|
||||||
|
|
||||||
|
class UserWiseCrossAttention(nn.Module):
|
||||||
|
"""
|
||||||
|
User-wise cross-attention decoder (논문 Section III-A, 식 4–6)
|
||||||
|
|
||||||
|
입력 Y : (n, U, d) 수신 임베딩 (잡음 포함)
|
||||||
|
출력 Ê : (n, U, d) 정제된 임베딩
|
||||||
|
복잡도 O(U d²)
|
||||||
|
"""
|
||||||
|
def __init__(self, d: int, U: int, H: int = 4):
|
||||||
|
super().__init__()
|
||||||
|
assert d % H == 0
|
||||||
|
self.d = d; self.U = U; self.H = H; self.dk = d // H
|
||||||
|
# JSAC convention: learnable per-user query vectors {q_u}, not signal-derived
|
||||||
|
self.q_vectors = nn.Parameter(torch.randn(U, d) * (d ** -0.5)) # (U, d)
|
||||||
|
self.eta = nn.Parameter(torch.ones(1)) # sharpness η
|
||||||
|
self.W_K = nn.Linear(d, d, bias=False)
|
||||||
|
self.W_V = nn.Linear(d, d, bias=False)
|
||||||
|
self.W_O = nn.Linear(d, d, bias=False)
|
||||||
|
self.norm = nn.LayerNorm(d)
|
||||||
|
# 학습 가능한 soft mask m_i ∈ [0,1]^d (식 4)
|
||||||
|
# 초기화: hard block mask logit (+3 = sigmoid → 0.95, -3 → 0.05)
|
||||||
|
# 이 초기화가 없으면 모든 mask = 0.5 → K feature가 모든 i에서 동일
|
||||||
|
# → softmax가 항상 uniform(1/U) → 학습 무력화
|
||||||
|
init_logits = torch.full((U, d), -3.0)
|
||||||
|
DPU = d // U
|
||||||
|
for u_ in range(U):
|
||||||
|
init_logits[u_, u_ * DPU:(u_ + 1) * DPU] = 3.0 # own block 강조
|
||||||
|
self.mask_logits = nn.Parameter(init_logits)
|
||||||
|
|
||||||
|
def forward(self, Y):
|
||||||
|
"""Y : (n, U, d) → (Ê, alpha)"""
|
||||||
|
n, U, d = Y.shape
|
||||||
|
masks = torch.sigmoid(self.mask_logits) # (U, d)
|
||||||
|
|
||||||
|
# R_{u,i} = y_u ⊙ m_i → (n, U_q, U_k, d)
|
||||||
|
R = Y.unsqueeze(2) * masks[None, None, :, :] # broadcast
|
||||||
|
|
||||||
|
# JSAC: q_u is a fixed learnable vector per user, independent of received signal
|
||||||
|
Q = self.q_vectors.unsqueeze(0).expand(n, -1, -1) # (n, U, d)
|
||||||
|
K = self.W_K(R) # (n, U, U, d)
|
||||||
|
V = self.W_V(R)
|
||||||
|
|
||||||
|
# multi-head reshape
|
||||||
|
def mh(t):
|
||||||
|
return t.reshape(*t.shape[:-1], self.H, self.dk)
|
||||||
|
|
||||||
|
Q_ = mh(Q) # (n,Uq,H,dk)
|
||||||
|
K_ = mh(K) # (n,Uq,Uk,H,dk)
|
||||||
|
V_ = mh(V)
|
||||||
|
|
||||||
|
# score : q_u^T k_i / sqrt(dk)
|
||||||
|
Q_e = Q_.unsqueeze(3) # (n,Uq,H,1,dk)
|
||||||
|
K_t = K_.permute(0, 1, 3, 2, 4) # (n,Uq,H,Uk,dk)
|
||||||
|
scores = self.eta * (Q_e * K_t).sum(-1) / (self.dk ** 0.5) # η * q_u^T k_i / √dk
|
||||||
|
alpha = F.softmax(scores, dim=-1) # (n,Uq,H,Uk)
|
||||||
|
|
||||||
|
V_t = V_.permute(0, 1, 3, 2, 4) # (n,Uq,H,Uk,dk)
|
||||||
|
ctx = (alpha.unsqueeze(-1) * V_t).sum(3) # (n,Uq,H,dk)
|
||||||
|
ctx = self.W_O(ctx.reshape(n, U, d)) # (n,Uq,d)
|
||||||
|
|
||||||
|
own = Y * masks[None, :, :] # 자기 유저 residual
|
||||||
|
out = F.normalize(self.norm(ctx + own), dim=-1)
|
||||||
|
return out, alpha.mean(0) # alpha: (Uq,H,Uk)
|
||||||
|
|
||||||
|
|
||||||
|
class SemanticCommSystem(nn.Module):
|
||||||
|
def __init__(self, d, U, H, decoder_only=False):
|
||||||
|
super().__init__()
|
||||||
|
self.encoder = IdentityEncoder() if decoder_only else SemanticEncoder(d)
|
||||||
|
self.decoder = UserWiseCrossAttention(d, U, H)
|
||||||
|
|
||||||
|
def forward(self, X, snr_db):
|
||||||
|
n, U, d = X.shape
|
||||||
|
E = self.encoder(X.reshape(n * U, d)).reshape(n, U, d)
|
||||||
|
Y = se_channel(E, snr_db) # always SE superposition channel
|
||||||
|
Ehat, alpha = self.decoder(Y)
|
||||||
|
return E, Ehat, alpha
|
||||||
|
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 4. LOSS (논문 식 8)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
def semantic_loss(Ehat, E, lam=0.1):
|
||||||
|
"""
|
||||||
|
L = (1/U)Σ(1 - cos(ê_u,e_u)) + λ Σ_{u≠v}|ρ(ê_u,ê_v)|
|
||||||
|
"""
|
||||||
|
cos = (Ehat * E).sum(-1)
|
||||||
|
distortion = (1 - cos).mean()
|
||||||
|
|
||||||
|
U = Ehat.shape[1]
|
||||||
|
emb = Ehat.mean(0) # (U, d)
|
||||||
|
ec = emb - emb.mean(1, keepdim=True)
|
||||||
|
en = F.normalize(ec, dim=1)
|
||||||
|
C = en @ en.T
|
||||||
|
mask = ~torch.eye(U, dtype=torch.bool, device=Ehat.device)
|
||||||
|
ortho = C[mask].abs().mean()
|
||||||
|
|
||||||
|
return distortion + lam * ortho, distortion.item(), ortho.item()
|
||||||
|
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 5. MAML TRAINER (논문 식 7, 9)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
class MAMLTrainer:
|
||||||
|
def __init__(self, model, cfg, device, rng, scenario_cfg=None):
|
||||||
|
self.model = model
|
||||||
|
self.cfg = cfg
|
||||||
|
self.device = device
|
||||||
|
self.rng = rng
|
||||||
|
self.scenario_cfg = scenario_cfg # None → default BLEND
|
||||||
|
mask_params = [p for n, p in model.named_parameters() if 'mask_logits' in n]
|
||||||
|
other_params = [p for n, p in model.named_parameters() if 'mask_logits' not in n]
|
||||||
|
self.optimizer = torch.optim.Adam([
|
||||||
|
{'params': other_params, 'lr': cfg.outer_lr},
|
||||||
|
{'params': mask_params, 'lr': cfg.outer_lr * 100},
|
||||||
|
])
|
||||||
|
self.snr_tasks = np.arange(cfg.snr_min, cfg.snr_max + 1e-6, cfg.snr_step)
|
||||||
|
|
||||||
|
def _inner_adapt(self, snr):
|
||||||
|
"""Inner-loop: adapted copy of model for task T_k = SNR γ_k"""
|
||||||
|
adapted = copy.deepcopy(self.model)
|
||||||
|
opt_in = torch.optim.SGD(adapted.parameters(), lr=self.cfg.inner_lr)
|
||||||
|
for _ in range(self.cfg.inner_steps):
|
||||||
|
X = gen_embeddings(self.cfg.batch, self.cfg.d,
|
||||||
|
self.cfg.U, self.rng,
|
||||||
|
self.scenario_cfg).to(self.device)
|
||||||
|
E, Ehat, _ = adapted(X, snr)
|
||||||
|
loss, _, _ = semantic_loss(Ehat, E, self.cfg.lam)
|
||||||
|
opt_in.zero_grad(); loss.backward(); opt_in.step()
|
||||||
|
return adapted
|
||||||
|
|
||||||
|
def meta_step(self):
|
||||||
|
"""Outer-loop: first-order MAML (Reptile-style aggregation)"""
|
||||||
|
fo_loss = torch.tensor(0.0, device=self.device)
|
||||||
|
for snr in self.snr_tasks:
|
||||||
|
X = gen_embeddings(self.cfg.batch, self.cfg.d,
|
||||||
|
self.cfg.U, self.rng,
|
||||||
|
self.scenario_cfg).to(self.device)
|
||||||
|
E, Ehat, _ = self.model(X, float(snr))
|
||||||
|
loss, _, _ = semantic_loss(Ehat, E, self.cfg.lam)
|
||||||
|
fo_loss = fo_loss + loss
|
||||||
|
fo_loss = fo_loss / len(self.snr_tasks)
|
||||||
|
self.optimizer.zero_grad()
|
||||||
|
fo_loss.backward()
|
||||||
|
nn.utils.clip_grad_norm_(self.model.parameters(), 5.0)
|
||||||
|
self.optimizer.step()
|
||||||
|
return fo_loss.item()
|
||||||
|
|
||||||
|
def train(self):
|
||||||
|
history = []
|
||||||
|
print("=" * 60)
|
||||||
|
print(f"MAML Training ({self.cfg.meta_epochs} epochs, "
|
||||||
|
f"{len(self.snr_tasks)} SNR tasks)")
|
||||||
|
print("=" * 60)
|
||||||
|
for ep in range(1, self.cfg.meta_epochs + 1):
|
||||||
|
loss = self.meta_step()
|
||||||
|
history.append(loss)
|
||||||
|
if ep % max(1, self.cfg.meta_epochs // 6) == 0:
|
||||||
|
print(f" Epoch {ep:4d}/{self.cfg.meta_epochs} loss={loss:.4f}")
|
||||||
|
return history
|
||||||
|
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 6. JOINT TRAINING BASELINE
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
def train_joint(model, cfg, device, rng, scenario_cfg=None):
|
||||||
|
mask_params = [p for n, p in model.named_parameters() if 'mask_logits' in n]
|
||||||
|
other_params = [p for n, p in model.named_parameters() if 'mask_logits' not in n]
|
||||||
|
opt = torch.optim.Adam([
|
||||||
|
{'params': other_params, 'lr': cfg.outer_lr},
|
||||||
|
{'params': mask_params, 'lr': cfg.outer_lr * 100},
|
||||||
|
])
|
||||||
|
history = []
|
||||||
|
print("=" * 60)
|
||||||
|
print(f"Joint Training ({cfg.joint_epochs} epochs)")
|
||||||
|
print("=" * 60)
|
||||||
|
for ep in range(1, cfg.joint_epochs + 1):
|
||||||
|
snr = float(np.random.uniform(cfg.snr_min, cfg.snr_max))
|
||||||
|
X = gen_embeddings(cfg.batch, cfg.d, cfg.U, rng, scenario_cfg).to(device)
|
||||||
|
E, Ehat, _ = model(X, snr)
|
||||||
|
loss, _, _ = semantic_loss(Ehat, E, cfg.lam)
|
||||||
|
opt.zero_grad(); loss.backward()
|
||||||
|
nn.utils.clip_grad_norm_(model.parameters(), 5.0)
|
||||||
|
opt.step()
|
||||||
|
history.append(loss.item())
|
||||||
|
if ep % max(1, cfg.joint_epochs // 6) == 0:
|
||||||
|
print(f" Epoch {ep:4d}/{cfg.joint_epochs} loss={loss.item():.4f}")
|
||||||
|
return history
|
||||||
|
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 7. EVALUATION (semantic_sim.py 구조 그대로 유지)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
def _normalize_np(E):
|
||||||
|
return E / (np.linalg.norm(E, axis=-1, keepdims=True) + 1e-8)
|
||||||
|
|
||||||
|
def cos_mean(Eh, Egt):
|
||||||
|
return (Eh * Egt).sum(-1).mean()
|
||||||
|
|
||||||
|
def ser_total(Eh, Egt, tau):
|
||||||
|
return ((Eh * Egt).sum(-1) < tau).mean()
|
||||||
|
|
||||||
|
def ser_per_user(Eh, Egt, tau):
|
||||||
|
return ((Eh * Egt).sum(-1) < tau).mean(0) # (U,)
|
||||||
|
|
||||||
|
def corr_matrix(Eh):
|
||||||
|
e = Eh.mean(0) # (U, D)
|
||||||
|
ec = e - e.mean(1, keepdims=True)
|
||||||
|
en = ec / (np.linalg.norm(ec, axis=1, keepdims=True) + 1e-8)
|
||||||
|
return en @ en.T # (U, U)
|
||||||
|
|
||||||
|
|
||||||
|
@torch.no_grad()
|
||||||
|
def evaluate_model(model, cfg, device, rng, channel="se", scenario_cfg=None):
|
||||||
|
"""
|
||||||
|
SNR_DB 전 구간에 걸쳐 SER / cosine / per-user SER 계산.
|
||||||
|
semantic_sim.py의 시뮬레이션 루프와 동일한 방식.
|
||||||
|
"""
|
||||||
|
SNR_DB = np.arange(cfg.snr_min, cfg.snr_max + 1e-6, cfg.snr_step)
|
||||||
|
model.eval()
|
||||||
|
res = {"ser": [], "cos": [], "sp": [], "rho": []}
|
||||||
|
|
||||||
|
for snr in SNR_DB:
|
||||||
|
ser_acc = 0.0; cos_acc = 0.0
|
||||||
|
sp_acc = np.zeros(cfg.U); rho_acc = []
|
||||||
|
|
||||||
|
for _ in range(cfg.n_mc):
|
||||||
|
Egt = gen_embeddings(cfg.batch, cfg.d, cfg.U, rng, scenario_cfg)
|
||||||
|
X = Egt.to(device)
|
||||||
|
|
||||||
|
# 채널 통과 — 모든 방식이 SE 수퍼포지션 채널 공유
|
||||||
|
if channel == "ofdma":
|
||||||
|
# SE-OFDMA: 같은 SE 채널, own D/U block만 사용
|
||||||
|
E = F.normalize(X.reshape(-1, cfg.d), dim=-1).reshape(-1, cfg.U, cfg.d)
|
||||||
|
Y = se_channel(E, float(snr))
|
||||||
|
Ehat = se_ofdma_decode(Y)
|
||||||
|
elif channel == "noma":
|
||||||
|
# SE-NOMA-SIC: full-band power-domain superposition (own channel + SIC)
|
||||||
|
E = F.normalize(X.reshape(-1, cfg.d), dim=-1).reshape(-1, cfg.U, cfg.d)
|
||||||
|
Ehat = se_noma_decode(E, float(snr))
|
||||||
|
elif channel == "sfdma":
|
||||||
|
# SFDMA: full-band, orthogonal semantic-subspace division (Ma 2024)
|
||||||
|
E = F.normalize(X.reshape(-1, cfg.d), dim=-1).reshape(-1, cfg.U, cfg.d)
|
||||||
|
Ehat = se_sfdma_decode(E, float(snr))
|
||||||
|
else:
|
||||||
|
# MAML+Attn / Joint+Attn: SE 채널 + cross-attention decoder
|
||||||
|
E, Ehat, _ = model(X, float(snr))
|
||||||
|
|
||||||
|
Eh = Ehat.cpu().numpy()
|
||||||
|
Egt_np = E.cpu().numpy()
|
||||||
|
|
||||||
|
ser_acc += ser_total(Eh, Egt_np, cfg.tau)
|
||||||
|
cos_acc += cos_mean(Eh, Egt_np)
|
||||||
|
sp_acc += ser_per_user(Eh, Egt_np, cfg.tau)
|
||||||
|
rho_acc.append(corr_matrix(Eh))
|
||||||
|
|
||||||
|
res["ser"].append(ser_acc / cfg.n_mc)
|
||||||
|
res["cos"].append(cos_acc / cfg.n_mc)
|
||||||
|
res["sp"].append(sp_acc / cfg.n_mc)
|
||||||
|
res["rho"].append(np.mean(rho_acc, axis=0))
|
||||||
|
|
||||||
|
for k in res:
|
||||||
|
res[k] = np.array(res[k])
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
@torch.no_grad()
|
||||||
|
def get_attention_map(model, snr, cfg, device, rng):
|
||||||
|
model.eval()
|
||||||
|
attn_sum = None
|
||||||
|
for _ in range(cfg.n_mc):
|
||||||
|
X = gen_embeddings(cfg.batch, cfg.d, cfg.U, rng).to(device)
|
||||||
|
E = model.encoder(X.reshape(-1, cfg.d)).reshape(-1, cfg.U, cfg.d)
|
||||||
|
Y = se_channel(E, float(snr))
|
||||||
|
_, alpha = model.decoder(Y) # (Uq, H, Uk)
|
||||||
|
a = alpha.mean(1).cpu().numpy() # (Uq, Uk) — avg over heads
|
||||||
|
attn_sum = a if attn_sum is None else attn_sum + a
|
||||||
|
return attn_sum / cfg.n_mc
|
||||||
|
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 8. 9-PANEL FIGURE (semantic_sim.py 그대로 재현)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
def plot_results(SNR_DB, res_maml, res_joint, res_ofdma, res_noma,
|
||||||
|
rho_j, rho_m, attn_m_mc, out_path, cfg):
|
||||||
|
|
||||||
|
U = cfg.UOF
|
||||||
|
KP = 'MAML+Attn\n(제안)'
|
||||||
|
MCFG = {
|
||||||
|
'OFDMA': ('#546E7A', 's--', 1.6, 'OFDMA'),
|
||||||
|
'NOMA-SIC': ('#E65100', '^-.', 1.6, 'NOMA-SIC'),
|
||||||
|
'Joint+Attn': ('#C62828', 'D--', 1.8, 'Joint+Attn'),
|
||||||
|
KP: ('#1565C0', 'o-', 2.5, 'MAML+Attn (제안)'),
|
||||||
|
}
|
||||||
|
res_all = {'OFDMA': res_ofdma, 'NOMA-SIC': res_noma,
|
||||||
|
'Joint+Attn': res_joint, KP: res_maml}
|
||||||
|
|
||||||
|
cmap_r = LinearSegmentedColormap.from_list(
|
||||||
|
'r', ['#1565C0', '#FFFFFF', '#C62828'], N=256)
|
||||||
|
cmap_a = LinearSegmentedColormap.from_list(
|
||||||
|
'a', ['#F5F5F5', '#1565C0'], N=256)
|
||||||
|
|
||||||
|
fig = plt.figure(figsize=(18, 15))
|
||||||
|
fig.patch.set_facecolor('#F8F9FA')
|
||||||
|
gs = gridspec.GridSpec(3, 3, figure=fig, hspace=0.48, wspace=0.38,
|
||||||
|
left=0.07, right=0.97, top=0.93, bottom=0.06)
|
||||||
|
|
||||||
|
idx10 = int((10 - cfg.snr_min) / cfg.snr_step) # SNR=10dB 인덱스
|
||||||
|
|
||||||
|
# (a) SER vs SNR
|
||||||
|
ax = fig.add_subplot(gs[0, 0]); ax.set_facecolor('white')
|
||||||
|
for k, (c, mk, lw, lb) in MCFG.items():
|
||||||
|
ax.semilogy(SNR_DB, res_all[k]['ser'], mk, lw=lw, ms=6, color=c, label=lb)
|
||||||
|
d10 = res_ofdma['ser'][idx10] - res_maml['ser'][idx10]
|
||||||
|
ax.annotate(f'Δ={d10:.3f}\n@ 10 dB',
|
||||||
|
xy=(10, res_maml['ser'][idx10]),
|
||||||
|
xytext=(13, res_maml['ser'][idx10] * 4),
|
||||||
|
fontsize=8.5, color='#1565C0',
|
||||||
|
arrowprops=dict(arrowstyle='->', color='#1565C0', lw=1.2))
|
||||||
|
ax.set_xlabel('SNR (dB)', fontsize=11); ax.set_ylabel('SER', fontsize=11)
|
||||||
|
ax.set_title('(a) SER vs SNR', fontsize=12, fontweight='bold')
|
||||||
|
ax.legend(fontsize=9); ax.grid(True, alpha=0.35); ax.set_xlim(0, 20)
|
||||||
|
|
||||||
|
# (b) 코사인 유사도
|
||||||
|
ax = fig.add_subplot(gs[0, 1]); ax.set_facecolor('white')
|
||||||
|
for k, (c, mk, lw, lb) in MCFG.items():
|
||||||
|
ax.plot(SNR_DB, res_all[k]['cos'], mk, lw=lw, ms=6, color=c, label=lb)
|
||||||
|
ax.axhline(cfg.tau, color='gray', lw=1.2, ls=':', label=f'τ={cfg.tau}')
|
||||||
|
ax.set_xlabel('SNR (dB)', fontsize=11); ax.set_ylabel('코사인 유사도', fontsize=11)
|
||||||
|
ax.set_title('(b) 코사인 유사도 vs SNR', fontsize=12, fontweight='bold')
|
||||||
|
ax.legend(fontsize=9); ax.grid(True, alpha=0.35)
|
||||||
|
ax.set_xlim(0, 20); ax.set_ylim(0.35, 1.02)
|
||||||
|
|
||||||
|
# (c) SER 개선량
|
||||||
|
ax = fig.add_subplot(gs[0, 2]); ax.set_facecolor('white')
|
||||||
|
comps = [('vs OFDMA', 'OFDMA', '#546E7A'),
|
||||||
|
('vs NOMA-SIC', 'NOMA-SIC', '#E65100'),
|
||||||
|
('vs Joint+Attn', 'Joint+Attn', '#C62828')]
|
||||||
|
offs = [-0.3, 0.0, 0.3]
|
||||||
|
for (lb, base, col), off in zip(comps, offs):
|
||||||
|
ax.bar(SNR_DB + off, res_all[base]['ser'] - res_maml['ser'],
|
||||||
|
width=0.28, alpha=0.80, color=col, label=lb)
|
||||||
|
ax.axhline(0, color='black', lw=0.8)
|
||||||
|
ax.set_xlabel('SNR (dB)', fontsize=11); ax.set_ylabel('SER 개선량', fontsize=11)
|
||||||
|
ax.set_title('(c) SER 개선량 (베이스라인 − 제안)', fontsize=12, fontweight='bold')
|
||||||
|
ax.legend(fontsize=9); ax.grid(True, alpha=0.25, axis='y')
|
||||||
|
|
||||||
|
# (d) 제안 사용자별 SER
|
||||||
|
ax = fig.add_subplot(gs[1, 0]); ax.set_facecolor('white')
|
||||||
|
for ui in range(U):
|
||||||
|
ax.semilogy(SNR_DB, res_maml['sp'][:, ui], 'o-', lw=1.8, ms=5,
|
||||||
|
color=USER_COLORS[ui], label=USER_LABELS[ui])
|
||||||
|
ax.set_xlabel('SNR (dB)', fontsize=11); ax.set_ylabel('SER', fontsize=11)
|
||||||
|
ax.set_title('(d) 제안 — 사용자별 SER', fontsize=12, fontweight='bold')
|
||||||
|
ax.legend(fontsize=8); ax.grid(True, alpha=0.35); ax.set_xlim(0, 20)
|
||||||
|
|
||||||
|
# (e) OFDMA 사용자별 SER
|
||||||
|
ax = fig.add_subplot(gs[1, 1]); ax.set_facecolor('white')
|
||||||
|
for ui in range(U):
|
||||||
|
ax.semilogy(SNR_DB, res_ofdma['sp'][:, ui], 's--', lw=1.6, ms=5,
|
||||||
|
color=USER_COLORS[ui], label=USER_LABELS[ui])
|
||||||
|
ax.set_xlabel('SNR (dB)', fontsize=11); ax.set_ylabel('SER', fontsize=11)
|
||||||
|
ax.set_title('(e) OFDMA — 사용자별 SER', fontsize=12, fontweight='bold')
|
||||||
|
ax.legend(fontsize=8); ax.grid(True, alpha=0.35); ax.set_xlim(0, 20)
|
||||||
|
|
||||||
|
# (f) SER @ 10 dB 막대
|
||||||
|
ax = fig.add_subplot(gs[1, 2]); ax.set_facecolor('white')
|
||||||
|
ms = ['OFDMA', 'NOMA-SIC', 'Joint+Attn', KP]
|
||||||
|
s10 = [res_all[m]['ser'][idx10] for m in ms]
|
||||||
|
lb10 = ['OFDMA', 'NOMA-SIC', 'Joint\n+Attn', 'MAML+Attn\n(제안)']
|
||||||
|
c10 = ['#546E7A', '#E65100', '#C62828', '#1565C0']
|
||||||
|
bars = ax.bar(range(4), s10, color=c10, width=0.55,
|
||||||
|
edgecolor='white', linewidth=1.2)
|
||||||
|
ax.set_xticks(range(4)); ax.set_xticklabels(lb10, fontsize=9.5)
|
||||||
|
ax.set_ylabel('SER @ 10 dB', fontsize=11)
|
||||||
|
ax.set_title('(f) 방법별 SER @ 10 dB', fontsize=12, fontweight='bold')
|
||||||
|
ax.grid(True, alpha=0.3, axis='y')
|
||||||
|
for b, v, c in zip(bars, s10, c10):
|
||||||
|
ax.text(b.get_x() + b.get_width() / 2, v + 0.003, f'{v:.3f}',
|
||||||
|
ha='center', va='bottom', fontsize=10, fontweight='bold', color=c)
|
||||||
|
|
||||||
|
# (g) 상관계수 — Joint
|
||||||
|
ax = fig.add_subplot(gs[2, 0]); ax.set_facecolor('white')
|
||||||
|
im = ax.imshow(rho_j, cmap=cmap_r, vmin=-0.3, vmax=0.3, aspect='auto')
|
||||||
|
ax.set_xticks(range(U)); ax.set_yticks(range(U))
|
||||||
|
ax.set_xticklabels([f'U{i+1}' for i in range(U)], fontsize=10)
|
||||||
|
ax.set_yticklabels([f'U{i+1}' for i in range(U)], fontsize=10)
|
||||||
|
for i in range(U):
|
||||||
|
for j in range(U):
|
||||||
|
v = rho_j[i, j]
|
||||||
|
ax.text(j, i, f'{v:.3f}', ha='center', va='center', fontsize=11,
|
||||||
|
fontweight='bold', color='white' if abs(v) > 0.15 else 'black')
|
||||||
|
plt.colorbar(im, ax=ax, fraction=0.046)
|
||||||
|
ax.set_title('(g) 상관계수 — Joint Training', fontsize=12, fontweight='bold')
|
||||||
|
ax.set_xlabel('사용자 j', fontsize=10); ax.set_ylabel('사용자 i', fontsize=10)
|
||||||
|
|
||||||
|
# (h) 상관계수 — MAML
|
||||||
|
ax = fig.add_subplot(gs[2, 1]); ax.set_facecolor('white')
|
||||||
|
im = ax.imshow(rho_m, cmap=cmap_r, vmin=-0.3, vmax=0.3, aspect='auto')
|
||||||
|
ax.set_xticks(range(U)); ax.set_yticks(range(U))
|
||||||
|
ax.set_xticklabels([f'U{i+1}' for i in range(U)], fontsize=10)
|
||||||
|
ax.set_yticklabels([f'U{i+1}' for i in range(U)], fontsize=10)
|
||||||
|
for i in range(U):
|
||||||
|
for j in range(U):
|
||||||
|
v = rho_m[i, j]
|
||||||
|
ax.text(j, i, f'{v:.3f}', ha='center', va='center', fontsize=11,
|
||||||
|
fontweight='bold', color='white' if abs(v) > 0.15 else 'black')
|
||||||
|
plt.colorbar(im, ax=ax, fraction=0.046)
|
||||||
|
ax.set_title('(h) 상관계수 — MAML (제안)', fontsize=12, fontweight='bold')
|
||||||
|
ax.set_xlabel('사용자 j', fontsize=10); ax.set_ylabel('사용자 i', fontsize=10)
|
||||||
|
|
||||||
|
# (i) Attention heatmap
|
||||||
|
ax = fig.add_subplot(gs[2, 2]); ax.set_facecolor('white')
|
||||||
|
im = ax.imshow(attn_m_mc, cmap=cmap_a,
|
||||||
|
vmin=0, vmax=attn_m_mc.max(), aspect='auto')
|
||||||
|
sh = ['보행자\n(U1)', '신호등\n(U2)', '차선\n(U3)', '속도\n(U4)']
|
||||||
|
ax.set_xticks(range(U)); ax.set_yticks(range(U))
|
||||||
|
ax.set_xticklabels(sh[:U], fontsize=9); ax.set_yticklabels(sh[:U], fontsize=9)
|
||||||
|
for i in range(U):
|
||||||
|
for j in range(U):
|
||||||
|
v = attn_m_mc[i, j]
|
||||||
|
ax.text(j, i, f'{v:.3f}', ha='center', va='center', fontsize=11,
|
||||||
|
fontweight='bold',
|
||||||
|
color='white' if v > attn_m_mc.max() * 0.5 else '#0D1B3E')
|
||||||
|
plt.colorbar(im, ax=ax, fraction=0.046)
|
||||||
|
ax.set_title(f'(i) 어텐션 가중치 α_{{u,i}} — MAML @ 10dB',
|
||||||
|
fontsize=12, fontweight='bold')
|
||||||
|
ax.set_xlabel('참조 사용자 i', fontsize=10)
|
||||||
|
ax.set_ylabel('질의 사용자 u', fontsize=10)
|
||||||
|
|
||||||
|
fig.suptitle(
|
||||||
|
'Multi-User Semantic Communication: User-Wise Attention vs Orthogonal Allocation\n'
|
||||||
|
f'(자율주행 시나리오 — U={U}, d={cfg.d}, Rayleigh Fading)',
|
||||||
|
fontsize=13, fontweight='bold', y=0.97)
|
||||||
|
|
||||||
|
plt.savefig(out_path, dpi=150, bbox_inches='tight', facecolor='#F8F9FA')
|
||||||
|
plt.close()
|
||||||
|
print(f" 그림 저장 → {out_path}")
|
||||||
|
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 9. SUMMARY PRINT (semantic_sim.py 스타일)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
def print_summary(res_maml, res_joint, res_ofdma, res_noma,
|
||||||
|
rho_j, rho_m, attn_m, cfg):
|
||||||
|
SNR_DB = np.arange(cfg.snr_min, cfg.snr_max + 1e-6, cfg.snr_step)
|
||||||
|
idx = {4: int((4 - cfg.snr_min) / cfg.snr_step),
|
||||||
|
10: int((10 - cfg.snr_min) / cfg.snr_step),
|
||||||
|
16: int((16 - cfg.snr_min) / cfg.snr_step)}
|
||||||
|
U = cfg.U
|
||||||
|
mask = ~np.eye(U, dtype=bool)
|
||||||
|
|
||||||
|
print("\n" + "=" * 62)
|
||||||
|
print("NUMERICAL SUMMARY")
|
||||||
|
print("=" * 62)
|
||||||
|
print(f"{'Method':<22}{'SER@4dB':>9}{'SER@10dB':>10}"
|
||||||
|
f"{'SER@16dB':>10}{'Cos@10dB':>10}")
|
||||||
|
print("-" * 62)
|
||||||
|
for lb, res in [('OFDMA', res_ofdma), ('NOMA-SIC', res_noma),
|
||||||
|
('Joint+Attn', res_joint), ('MAML+Attn (제안)', res_maml)]:
|
||||||
|
print(f"{lb:<22}{res['ser'][idx[4]]:>9.4f}"
|
||||||
|
f"{res['ser'][idx[10]]:>10.4f}"
|
||||||
|
f"{res['ser'][idx[16]]:>10.4f}"
|
||||||
|
f"{res['cos'][idx[10]]:>10.4f}")
|
||||||
|
|
||||||
|
print(f"\n임베딩 상관계수 |ρ| (off-diag @ 10 dB):")
|
||||||
|
print(f" Joint : mean={np.abs(rho_j[mask]).mean():.4f} "
|
||||||
|
f"[{rho_j[mask].min():.4f}, {rho_j[mask].max():.4f}]")
|
||||||
|
print(f" MAML : mean={np.abs(rho_m[mask]).mean():.4f} "
|
||||||
|
f"[{rho_m[mask].min():.4f}, {rho_m[mask].max():.4f}]")
|
||||||
|
|
||||||
|
print(f"\n어텐션 가중치 α (MAML @ 10 dB):")
|
||||||
|
hdr = ''.join([f" U{j+1}" for j in range(U)])
|
||||||
|
print(f"{'':>14}{hdr}")
|
||||||
|
names = ['보행자', '신호등', '차선 ', '속도 ']
|
||||||
|
for i in range(U):
|
||||||
|
row = ''.join([f" {attn_m[i,j]:>7.4f}" for j in range(U)])
|
||||||
|
nm = names[i] if i < len(names) else f'U{i+1} '
|
||||||
|
print(f" U{i+1}({nm}){row}")
|
||||||
|
|
||||||
|
print(f"\n핵심: α[보행자→신호등]={attn_m[0,1]:.4f} (높음) vs "
|
||||||
|
f"α[보행자→속도]={attn_m[0,3]:.4f} (낮음)")
|
||||||
|
print(f" SER 개선 vs OFDMA @ 10dB: "
|
||||||
|
f"{res_ofdma['ser'][idx[10]] - res_maml['ser'][idx[10]]:.4f}")
|
||||||
|
print("=" * 62)
|
||||||
|
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 10. JSON EXPORT (for overlay in semantic_correlation_sim.py)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
import json
|
||||||
|
|
||||||
|
def export_results_json(scenario: str, SNR_DB, res_maml, res_joint,
|
||||||
|
res_ofdma, res_noma, outdir: Path):
|
||||||
|
"""Export SER results to JSON so semantic_correlation_sim.py can overlay them."""
|
||||||
|
data = {
|
||||||
|
"scenario": scenario,
|
||||||
|
"decoder_only": True,
|
||||||
|
"snr_db": SNR_DB.tolist(),
|
||||||
|
"maml_ser": res_maml["ser"].tolist(),
|
||||||
|
"joint_ser": res_joint["ser"].tolist(),
|
||||||
|
"ofdma_ser": res_ofdma["ser"].tolist(),
|
||||||
|
"noma_ser": res_noma["ser"].tolist(),
|
||||||
|
}
|
||||||
|
path = outdir / f"trained_{scenario}.json"
|
||||||
|
with open(path, "w") as f:
|
||||||
|
json.dump(data, f, indent=2)
|
||||||
|
print(f"결과 JSON 저장 → {path}")
|
||||||
|
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 11. MAIN
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
def _run_one_scenario(scenario: str, cfg, device, np_rng, outdir: Path):
|
||||||
|
"""Train + evaluate one scenario; save checkpoint + JSON."""
|
||||||
|
scenario_cfg = SCENARIO_CONFIGS.get(scenario) # None for DEFAULT falls back to BLEND
|
||||||
|
|
||||||
|
def make_model():
|
||||||
|
return SemanticCommSystem(cfg.d, cfg.U, cfg.H,
|
||||||
|
decoder_only=cfg.decoder_only).to(device)
|
||||||
|
|
||||||
|
maml_model = make_model()
|
||||||
|
joint_model = make_model()
|
||||||
|
|
||||||
|
SNR_DB = np.arange(cfg.snr_min, cfg.snr_max + 1e-6, cfg.snr_step)
|
||||||
|
|
||||||
|
# ── 학습 ──────────────────────────────────────────────────────────
|
||||||
|
tag = f"[{scenario}]"
|
||||||
|
print(f"\n{'='*60}\n{tag} decoder_only={cfg.decoder_only}\n{'='*60}")
|
||||||
|
|
||||||
|
trainer = MAMLTrainer(maml_model, cfg, device, np_rng, scenario_cfg)
|
||||||
|
hist_m = trainer.train()
|
||||||
|
hist_j = train_joint(joint_model, cfg, device, np_rng, scenario_cfg)
|
||||||
|
|
||||||
|
ckpt_path = outdir / f"models_{scenario}.pt"
|
||||||
|
torch.save({"maml": maml_model.state_dict(),
|
||||||
|
"joint": joint_model.state_dict()}, ckpt_path)
|
||||||
|
print(f"체크포인트 저장 → {ckpt_path}")
|
||||||
|
|
||||||
|
# 학습 곡선
|
||||||
|
fig, ax = plt.subplots(figsize=(7, 4))
|
||||||
|
ax.plot(hist_m, label='MAML outer loss', color='#1565C0', lw=1.5)
|
||||||
|
ax.plot(hist_j, label='Joint loss', color='#C62828', lw=1.5, ls='--')
|
||||||
|
ax.set_xlabel('Epoch'); ax.set_ylabel('Loss')
|
||||||
|
ax.set_title(f'Training Loss — {scenario}')
|
||||||
|
ax.legend(); ax.grid(True, alpha=0.35); fig.tight_layout()
|
||||||
|
fig.savefig(str(outdir / f"training_curves_{scenario}.png"), dpi=150)
|
||||||
|
plt.close()
|
||||||
|
|
||||||
|
# ── 평가 ──────────────────────────────────────────────────────────
|
||||||
|
print(f"{tag} 평가 중 ...")
|
||||||
|
res_maml = evaluate_model(maml_model, cfg, device, np_rng, "rayleigh", scenario_cfg)
|
||||||
|
res_joint = evaluate_model(joint_model, cfg, device, np_rng, "rayleigh", scenario_cfg)
|
||||||
|
res_ofdma = evaluate_model(joint_model, cfg, device, np_rng, "ofdma", scenario_cfg)
|
||||||
|
res_noma = evaluate_model(joint_model, cfg, device, np_rng, "noma", scenario_cfg)
|
||||||
|
|
||||||
|
export_results_json(scenario, SNR_DB, res_maml, res_joint,
|
||||||
|
res_ofdma, res_noma, outdir)
|
||||||
|
|
||||||
|
# SNR=10dB 상관계수 & attention map
|
||||||
|
idx10 = int((10 - cfg.snr_min) / cfg.snr_step)
|
||||||
|
rho_j = res_joint["rho"][idx10]
|
||||||
|
rho_m = res_maml["rho"][idx10]
|
||||||
|
attn_m = get_attention_map(maml_model, 10.0, cfg, device, np_rng)
|
||||||
|
|
||||||
|
print_summary(res_maml, res_joint, res_ofdma, res_noma,
|
||||||
|
rho_j, rho_m, attn_m, cfg)
|
||||||
|
|
||||||
|
# 단일 시나리오 그림 저장 (DEFAULT는 기존 filename 유지)
|
||||||
|
suffix = "" if scenario == "DEFAULT" else f"_{scenario}"
|
||||||
|
fig_path = str(outdir / f"semantic_results{suffix}.png")
|
||||||
|
plot_results(SNR_DB, res_maml, res_joint, res_ofdma, res_noma,
|
||||||
|
rho_j, rho_m, attn_m, fig_path, cfg)
|
||||||
|
return res_maml, res_joint, res_ofdma, res_noma
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
cfg = get_cfg()
|
||||||
|
torch.manual_seed(cfg.seed)
|
||||||
|
np_rng = np.random.default_rng(cfg.seed)
|
||||||
|
|
||||||
|
if cfg.device == "auto":
|
||||||
|
device = torch.device(
|
||||||
|
"cuda" if torch.cuda.is_available() else
|
||||||
|
"mps" if torch.backends.mps.is_available() else
|
||||||
|
"cpu")
|
||||||
|
else:
|
||||||
|
device = torch.device(cfg.device)
|
||||||
|
print(f"Device: {device} | d={cfg.d}, U={cfg.U}, "
|
||||||
|
f"meta_epochs={cfg.meta_epochs}, n_mc={cfg.n_mc}, "
|
||||||
|
f"scenario={cfg.scenario}, decoder_only={cfg.decoder_only}")
|
||||||
|
|
||||||
|
outdir = Path(cfg.outdir)
|
||||||
|
outdir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# eval_only mode (legacy)
|
||||||
|
if cfg.eval_only and cfg.ckpt:
|
||||||
|
SNR_DB = np.arange(cfg.snr_min, cfg.snr_max + 1e-6, cfg.snr_step)
|
||||||
|
scenario_cfg = SCENARIO_CONFIGS.get(cfg.scenario)
|
||||||
|
maml_model = SemanticCommSystem(cfg.d, cfg.U, cfg.H,
|
||||||
|
decoder_only=cfg.decoder_only).to(device)
|
||||||
|
joint_model = SemanticCommSystem(cfg.d, cfg.U, cfg.H,
|
||||||
|
decoder_only=cfg.decoder_only).to(device)
|
||||||
|
ck = torch.load(cfg.ckpt, map_location=device)
|
||||||
|
maml_model.load_state_dict(ck["maml"])
|
||||||
|
joint_model.load_state_dict(ck["joint"])
|
||||||
|
print(f"체크포인트 로드 ← {cfg.ckpt}")
|
||||||
|
res_maml = evaluate_model(maml_model, cfg, device, np_rng, "rayleigh", scenario_cfg)
|
||||||
|
res_joint = evaluate_model(joint_model, cfg, device, np_rng, "rayleigh", scenario_cfg)
|
||||||
|
res_ofdma = evaluate_model(joint_model, cfg, device, np_rng, "ofdma", scenario_cfg)
|
||||||
|
res_noma = evaluate_model(joint_model, cfg, device, np_rng, "noma", scenario_cfg)
|
||||||
|
export_results_json(cfg.scenario, SNR_DB, res_maml, res_joint,
|
||||||
|
res_ofdma, res_noma, outdir)
|
||||||
|
print(f"\n완료! → {outdir}/")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Train / eval scenarios
|
||||||
|
if cfg.scenario.upper() == "ALL":
|
||||||
|
scenarios = ['HIGH', 'LOW', 'MIX', 'HETERO', 'ASYM']
|
||||||
|
else:
|
||||||
|
scenarios = [cfg.scenario]
|
||||||
|
|
||||||
|
for sc in scenarios:
|
||||||
|
_run_one_scenario(sc, cfg, device, np_rng, outdir)
|
||||||
|
|
||||||
|
print(f"\n완료! → {outdir}/")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Executable
+1044
File diff suppressed because it is too large
Load Diff
Executable
+124
@@ -0,0 +1,124 @@
|
|||||||
|
"""
|
||||||
|
Ablation / sensitivity studies for the TWC revision.
|
||||||
|
Part 1 (torch): H (heads), K (number of SNR meta-tasks), S (inner steps)
|
||||||
|
sensitivity -> SER@10dB, cos@10dB, |rho_off|@10dB on HIGH.
|
||||||
|
Part 2 (numpy): refined scalability -> mean cosine + top-k retention vs U.
|
||||||
|
"""
|
||||||
|
import types
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
import maml_semantic as M
|
||||||
|
|
||||||
|
torch.manual_seed(0)
|
||||||
|
|
||||||
|
class _Sim: # minimal stand-ins (avoid importing the heavy sim module)
|
||||||
|
BATCH = 64
|
||||||
|
@staticmethod
|
||||||
|
def _norm(E):
|
||||||
|
return E / (np.linalg.norm(E, axis=-1, keepdims=True) + 1e-8)
|
||||||
|
@staticmethod
|
||||||
|
def cos_sim(Eh, Eg):
|
||||||
|
return (Eh * Eg).sum(-1)
|
||||||
|
sim = _Sim()
|
||||||
|
|
||||||
|
def make_cfg(**kw):
|
||||||
|
base = dict(d=64, U=4, H=4, tau=0.45, lam=0.1, snr_min=0.0, snr_max=20.0,
|
||||||
|
snr_step=2.0, inner_lr=0.01, inner_steps=5, outer_lr=1e-3,
|
||||||
|
meta_epochs=70, joint_epochs=70, batch=64, n_mc=60, seed=42,
|
||||||
|
scenario='HIGH', decoder_only=True)
|
||||||
|
base.update(kw)
|
||||||
|
return types.SimpleNamespace(**base)
|
||||||
|
|
||||||
|
def train_eval(cfg):
|
||||||
|
device = 'cpu'
|
||||||
|
rng = np.random.default_rng(cfg.seed)
|
||||||
|
scen = M.SCENARIO_CONFIGS[cfg.scenario]
|
||||||
|
model = M.SemanticCommSystem(cfg.d, cfg.U, cfg.H, decoder_only=cfg.decoder_only).to(device)
|
||||||
|
tr = M.MAMLTrainer(model, cfg, device, rng, scen)
|
||||||
|
tr.train()
|
||||||
|
res = M.evaluate_model(model, cfg, device, rng, "rayleigh", scen)
|
||||||
|
snr = np.arange(cfg.snr_min, cfg.snr_max + 1e-6, cfg.snr_step)
|
||||||
|
i10 = int(np.argmin(np.abs(snr - 10)))
|
||||||
|
rho = res['rho'][i10]
|
||||||
|
mask = ~np.eye(cfg.U, dtype=bool)
|
||||||
|
return res['ser'][i10], res['cos'][i10], float(np.abs(rho[mask]).mean()), len(snr)
|
||||||
|
|
||||||
|
def part1():
|
||||||
|
print("\n=== ABLATION (HIGH scenario, decoder-only, SNR=10 dB) ===")
|
||||||
|
print("\n-- Attention heads H (d=64) --")
|
||||||
|
for H in [1, 2, 4, 8]:
|
||||||
|
ser, cos, rho, _ = train_eval(make_cfg(H=H))
|
||||||
|
print(f" H={H}: dk={64//H:2d} SER={ser:.3f} cos={cos:.3f} |rho_off|={rho:.3f}")
|
||||||
|
print("\n-- Number of SNR meta-tasks K (via snr_step) --")
|
||||||
|
for step in [20.0, 10.0, 4.0, 2.0, 1.0]:
|
||||||
|
ser, cos, rho, K = train_eval(make_cfg(snr_step=step))
|
||||||
|
print(f" K={K:2d} (step={step:>4}): SER={ser:.3f} cos={cos:.3f} |rho_off|={rho:.3f}")
|
||||||
|
print("\n-- Inner-loop steps S --")
|
||||||
|
for S in [1, 3, 5, 10]:
|
||||||
|
ser, cos, rho, _ = train_eval(make_cfg(inner_steps=S))
|
||||||
|
print(f" S={S:2d}: SER={ser:.3f} cos={cos:.3f} |rho_off|={rho:.3f}")
|
||||||
|
|
||||||
|
# ---- Part 2: refined scalability (cosine + top-k retention) ----
|
||||||
|
RNG = np.random.default_rng(11)
|
||||||
|
|
||||||
|
def gen_clustered(n, U, D, g, beta):
|
||||||
|
nc = U // g
|
||||||
|
scenes = [RNG.standard_normal(D) for _ in range(nc)]
|
||||||
|
scenes = [s / np.linalg.norm(s) for s in scenes]
|
||||||
|
embs = []
|
||||||
|
for u in range(U):
|
||||||
|
s = scenes[u // g]
|
||||||
|
priv = RNG.standard_normal((n, D)); priv /= np.linalg.norm(priv, axis=-1, keepdims=True) + 1e-8
|
||||||
|
e = np.sqrt(1 - beta**2) * priv + beta * s[None, :]
|
||||||
|
e /= np.linalg.norm(e, axis=-1, keepdims=True) + 1e-8
|
||||||
|
embs.append(e)
|
||||||
|
return np.stack(embs, 1)
|
||||||
|
|
||||||
|
def masks_for(U, D):
|
||||||
|
dpu = D // U; Mk = np.zeros((U, D))
|
||||||
|
for u in range(U):
|
||||||
|
Mk[u, u*dpu:(u+1)*dpu] = 1.0
|
||||||
|
return Mk
|
||||||
|
|
||||||
|
def se_chan(E, snr_db, Mk):
|
||||||
|
n, U, D = E.shape
|
||||||
|
Ytx = (E * Mk[None]).sum(1)
|
||||||
|
h = np.sqrt(RNG.standard_normal((n, U, 1))**2 + RNG.standard_normal((n, U, 1))**2) * np.sqrt(0.5)
|
||||||
|
nstd = np.sqrt(float(np.mean(Ytx**2)) / (10**(snr_db/10)))
|
||||||
|
return h * Ytx[:, None, :] + RNG.standard_normal((n, U, D)) * nstd
|
||||||
|
|
||||||
|
def attn(Yrx, Mk, bm, topk=None):
|
||||||
|
n, U, D = Yrx.shape
|
||||||
|
R = Yrx[:, :, None, :] * Mk[None, None]
|
||||||
|
a = bm.copy(); np.fill_diagonal(a, 1.0)
|
||||||
|
if topk is not None and topk < U:
|
||||||
|
for u in range(U):
|
||||||
|
order = np.argsort(-a[u]); keep = set(order[:topk]) | {u}
|
||||||
|
for v in range(U):
|
||||||
|
if v not in keep: a[u, v] = 0.0
|
||||||
|
a /= a.sum(1, keepdims=True) + 1e-8
|
||||||
|
ctx = np.einsum('ui,buid->bud', a, R)
|
||||||
|
return np.stack([sim._norm(ctx[:, u, :]) for u in range(U)], 1)
|
||||||
|
|
||||||
|
def part2():
|
||||||
|
print("\n=== SCALABILITY: cosine + top-k retention (g=4, beta=0.65, SNR=20 dB) ===")
|
||||||
|
g, beta, snr = 4, 0.65, 20.0
|
||||||
|
for U in [4, 8, 16, 32]:
|
||||||
|
D = 16 * U; Mk = masks_for(U, D)
|
||||||
|
bm = np.zeros((U, U))
|
||||||
|
for i in range(U):
|
||||||
|
for j in range(U):
|
||||||
|
if i // g == j // g: bm[i, j] = beta*beta
|
||||||
|
nmc = 150; cf = ct = 0.0
|
||||||
|
for _ in range(nmc):
|
||||||
|
E = gen_clustered(sim.BATCH, U, D, g, beta)
|
||||||
|
cf += sim.cos_sim(attn(se_chan(E, snr, Mk), Mk, bm), E).mean()
|
||||||
|
ct += sim.cos_sim(attn(se_chan(E, snr, Mk), Mk, bm, topk=g), E).mean()
|
||||||
|
cf /= nmc; ct /= nmc
|
||||||
|
print(f" U={U:3d} cos_full={cf:.3f} cos_topk(k={g})={ct:.3f} "
|
||||||
|
f"retention={100*ct/cf:5.1f}% ops full={U*U} topk={U*g} ({U//g}x)")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
part1()
|
||||||
|
part2()
|
||||||
|
print("\nDONE.")
|
||||||
Executable
+39
@@ -0,0 +1,39 @@
|
|||||||
|
"""Fig. 3 (SER gain vs relevance coefficient) regenerated with the SAME box
|
||||||
|
aspect (0.8) as the Fig. 2 panels, from results/data/beta_sweep.csv."""
|
||||||
|
import csv, numpy as np
|
||||||
|
import matplotlib; matplotlib.use('Agg')
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
rows = list(csv.DictReader(open('results/data/beta_sweep.csv')))
|
||||||
|
SNRS = [0.0, 5.0, 10.0]
|
||||||
|
STY = {0.0: ('#C62828', 's--', 'SNR = 0 dB'),
|
||||||
|
5.0: ('#E65100', '^-.', 'SNR = 5 dB'),
|
||||||
|
10.0: ('#1565C0', 'o-', 'SNR = 10 dB')}
|
||||||
|
FILL = {0.0: '#C62828', 5.0: '#E65100', 10.0: '#1565C0'}
|
||||||
|
|
||||||
|
data = {s: {'b': [], 'g': []} for s in SNRS}
|
||||||
|
for r in rows:
|
||||||
|
s = float(r['snr_label'])
|
||||||
|
if s in data:
|
||||||
|
data[s]['b'].append(float(r['beta_sq'])); data[s]['g'].append(float(r['gain_maml']))
|
||||||
|
for s in SNRS:
|
||||||
|
o = np.argsort(data[s]['b'])
|
||||||
|
data[s]['b'] = np.array(data[s]['b'])[o]; data[s]['g'] = np.array(data[s]['g'])[o]
|
||||||
|
|
||||||
|
fig, ax = plt.subplots(figsize=(5.2, 4.6))
|
||||||
|
for s in SNRS:
|
||||||
|
c, mk, lbl = STY[s]
|
||||||
|
ax.plot(data[s]['b'], data[s]['g'], mk, lw=2.0, color=c, label=lbl, markersize=5)
|
||||||
|
ax.fill_between(data[s]['b'], 0, data[s]['g'], alpha=0.07, color=FILL[s])
|
||||||
|
ax.axhline(0, color='gray', lw=0.8, ls=':')
|
||||||
|
ax.set_xlabel(r'Semantic relevance coefficient $\beta_{u,v}=\beta_u\cdot\beta_v$', fontsize=13)
|
||||||
|
ax.set_ylabel('SER gain over OFDMA', fontsize=13)
|
||||||
|
ax.tick_params(labelsize=12)
|
||||||
|
ax.legend(loc='upper left', fontsize=12)
|
||||||
|
ax.grid(True, alpha=0.3)
|
||||||
|
ax.set_xlim(-0.01, 0.82)
|
||||||
|
ax.set_box_aspect(0.8) # match the Fig. 2 panel box aspect
|
||||||
|
fig.tight_layout()
|
||||||
|
fig.savefig('results/fig4_beta_sweep.pdf', bbox_inches='tight')
|
||||||
|
fig.savefig('results/fig4_beta_sweep.png', dpi=150, bbox_inches='tight')
|
||||||
|
print('saved results/fig4_beta_sweep.pdf (box_aspect=0.8)')
|
||||||
Executable
+20
@@ -0,0 +1,20 @@
|
|||||||
|
import types, numpy as np, torch
|
||||||
|
import maml_semantic as M
|
||||||
|
torch.manual_seed(0)
|
||||||
|
def cfg(**k):
|
||||||
|
b=dict(d=64,U=4,H=4,tau=0.45,lam=0.1,snr_min=0.0,snr_max=20.0,snr_step=2.0,
|
||||||
|
inner_lr=0.01,inner_steps=5,outer_lr=1e-3,meta_epochs=70,joint_epochs=70,
|
||||||
|
batch=64,n_mc=60,seed=42,scenario='HIGH',decoder_only=True); b.update(k)
|
||||||
|
return types.SimpleNamespace(**b)
|
||||||
|
def run(d):
|
||||||
|
c=cfg(d=d); dev='cpu'; rng=np.random.default_rng(42); scen=M.SCENARIO_CONFIGS['HIGH']
|
||||||
|
m=M.SemanticCommSystem(c.d,c.U,c.H,decoder_only=True).to(dev)
|
||||||
|
M.MAMLTrainer(m,c,dev,rng,scen).train()
|
||||||
|
res=M.evaluate_model(m,c,dev,rng,'rayleigh',scen)
|
||||||
|
snr=np.arange(0,20.0001,2); i=int(np.argmin(np.abs(snr-10)))
|
||||||
|
rho=res['rho'][i]; mask=~np.eye(c.U,dtype=bool)
|
||||||
|
return res['ser'][i],res['cos'][i],float(np.abs(rho[mask]).mean())
|
||||||
|
print("=== d sweep (HIGH, SNR=10dB) ===")
|
||||||
|
for d in [32,64,128]:
|
||||||
|
s,co,r=run(d); print(f"d={d:3d}: SER={s:.3f} cos={co:.3f} |rho_off|={r:.3f}")
|
||||||
|
print("DONE")
|
||||||
Executable
+31
@@ -0,0 +1,31 @@
|
|||||||
|
"""End-to-end (learnable encoder) vs decoder-only MAML, HIGH scenario.
|
||||||
|
Verifies Reviewer-1 Comment-3 claim that cross-attention stays stable and
|
||||||
|
effective when the encoder is also learned."""
|
||||||
|
import types, numpy as np, torch
|
||||||
|
import maml_semantic as M
|
||||||
|
torch.manual_seed(0)
|
||||||
|
|
||||||
|
def cfg(**k):
|
||||||
|
b = dict(d=64, U=4, H=4, tau=0.45, lam=0.1, snr_min=0.0, snr_max=20.0,
|
||||||
|
snr_step=2.0, inner_lr=0.01, inner_steps=5, outer_lr=1e-3,
|
||||||
|
meta_epochs=120, joint_epochs=120, batch=64, n_mc=80, seed=42,
|
||||||
|
scenario='HIGH', decoder_only=True)
|
||||||
|
b.update(k); return types.SimpleNamespace(**b)
|
||||||
|
|
||||||
|
def run(decoder_only):
|
||||||
|
c = cfg(decoder_only=decoder_only); dev='cpu'; rng=np.random.default_rng(c.seed)
|
||||||
|
scen=M.SCENARIO_CONFIGS[c.scenario]
|
||||||
|
mdl=M.SemanticCommSystem(c.d,c.U,c.H,decoder_only=decoder_only).to(dev)
|
||||||
|
M.MAMLTrainer(mdl,c,dev,rng,scen).train()
|
||||||
|
res=M.evaluate_model(mdl,c,dev,rng,"rayleigh",scen)
|
||||||
|
snr=np.arange(0,20.0001,2);
|
||||||
|
def at(s): return res['ser'][int(np.argmin(np.abs(snr-s)))], res['cos'][int(np.argmin(np.abs(snr-s)))]
|
||||||
|
i10=int(np.argmin(np.abs(snr-10))); rho=res['rho'][i10]; mask=~np.eye(c.U,dtype=bool)
|
||||||
|
return at(4), at(10), at(16), float(np.abs(rho[mask]).mean())
|
||||||
|
|
||||||
|
print("=== END-TO-END (learnable encoder) vs DECODER-ONLY, HIGH ===")
|
||||||
|
for tag, do in [("decoder-only (frozen enc)", True), ("end-to-end (learnable enc)", False)]:
|
||||||
|
(s4,c4),(s10,c10),(s16,c16),rho = run(do)
|
||||||
|
print(f" {tag:30s} SER@4/10/16 = {s4:.3f}/{s10:.3f}/{s16:.3f} "
|
||||||
|
f"cos@10={c10:.3f} |rho_off|@10={rho:.3f}")
|
||||||
|
print("DONE.")
|
||||||
Executable
+197
@@ -0,0 +1,197 @@
|
|||||||
|
"""
|
||||||
|
Auxiliary experiments for the TWC revision (main_FFF.tex).
|
||||||
|
Reuses the validated simulation primitives in semantic_correlation_sim.py to
|
||||||
|
produce REAL numbers for the new reviewer-requested studies:
|
||||||
|
|
||||||
|
A. Threshold sensitivity (tau = 0.30..0.50) + mean cosine similarity [R1.2, R3.2]
|
||||||
|
B. Residual phase-error robustness [R1.1, R2.3]
|
||||||
|
C. User scaling U in {4,8,16,32} : full vs. sparse top-k attention [R1.6, R3.4]
|
||||||
|
D. DL semantic baseline: Joint+Attn calibrated at a single nominal SNR [R1.5,R2.5,R3.5]
|
||||||
|
vs. MAML SNR-adaptive shrinkage
|
||||||
|
|
||||||
|
All results are printed as LaTeX-ready rows.
|
||||||
|
"""
|
||||||
|
import numpy as np
|
||||||
|
import semantic_correlation_sim as sim
|
||||||
|
|
||||||
|
RNG = np.random.default_rng(2026)
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# A. Threshold sensitivity + mean cosine similarity (HIGH/LOW/MIX, SNR=10 dB)
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
def collect_cos(scenario_key, snr=10.0, n_mc=600):
|
||||||
|
cfg = sim.SCENARIOS[scenario_key]
|
||||||
|
beta_mat = sim.compute_beta_matrix(cfg)
|
||||||
|
out = {'OFDMA': [], 'NOMA-SIC': [], 'UWCA': []}
|
||||||
|
for _ in range(n_mc):
|
||||||
|
Egt = sim.gen_embeddings(sim.BATCH, scenario_key)
|
||||||
|
Y = sim.shared_embedding_channel(Egt, snr)
|
||||||
|
Eh, _ = sim.ofdma_se_decoder(Y)
|
||||||
|
out['OFDMA'].append(sim.cos_sim(Eh, Egt).ravel())
|
||||||
|
y, h = sim.noma_ul_channel(Egt, snr)
|
||||||
|
Eh = sim.noma_sic_decoder(y, h)
|
||||||
|
out['NOMA-SIC'].append(sim.cos_sim(Eh, Egt).ravel())
|
||||||
|
Y = sim.shared_embedding_channel(Egt, snr)
|
||||||
|
Eh, _ = sim.maml_attention_se_decoder(Y, snr, beta_mat)
|
||||||
|
out['UWCA'].append(sim.cos_sim(Eh, Egt).ravel())
|
||||||
|
return {k: np.concatenate(v) for k, v in out.items()}
|
||||||
|
|
||||||
|
def exp_A():
|
||||||
|
print("\n=== EXP A: threshold sensitivity + mean cosine (SNR=10 dB) ===")
|
||||||
|
taus = [0.30, 0.35, 0.40, 0.45, 0.50]
|
||||||
|
for scen in ['HIGH', 'LOW', 'MIX']:
|
||||||
|
cos = collect_cos(scen)
|
||||||
|
print(f"\n[{scen}]")
|
||||||
|
for m in ['OFDMA', 'NOMA-SIC', 'UWCA']:
|
||||||
|
c = cos[m]
|
||||||
|
sers = [f"{(c < t).mean():.3f}" for t in taus]
|
||||||
|
print(f" {m:9s} meancos={c.mean():.3f} SER@tau[{','.join(map(str,taus))}] = {sers}")
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# B. Residual phase-error robustness
|
||||||
|
# After imperfect pilot-based compensation, residual phase Dphi ~ N(0,sig^2);
|
||||||
|
# recovered in-phase component scales by cos(Dphi) (quadrature energy lost).
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
def se_channel_phase(E, snr_db, sigma_phi_deg):
|
||||||
|
n, U, D = E.shape
|
||||||
|
X = E * sim.MASKS[None, :, :]
|
||||||
|
Ytx = X.sum(axis=1)
|
||||||
|
h = (np.sqrt(RNG.standard_normal((n, U, 1))**2 + RNG.standard_normal((n, U, 1))**2)
|
||||||
|
* np.sqrt(0.5))
|
||||||
|
sig_power = float(np.mean(Ytx**2))
|
||||||
|
noise_std = np.sqrt(sig_power / (10**(snr_db/10)))
|
||||||
|
phi = np.deg2rad(sigma_phi_deg) * RNG.standard_normal((n, U, 1))
|
||||||
|
Yrx = h * np.cos(phi) * Ytx[:, None, :] + RNG.standard_normal((n, U, D)) * noise_std
|
||||||
|
return Yrx
|
||||||
|
|
||||||
|
def exp_B():
|
||||||
|
print("\n=== EXP B: residual phase-error robustness (HIGH scenario) ===")
|
||||||
|
cfg = sim.SCENARIOS['HIGH']; beta_mat = sim.compute_beta_matrix(cfg)
|
||||||
|
for snr in [10.0, 20.0]:
|
||||||
|
row = []
|
||||||
|
for sig in [0, 5, 10, 15, 20]:
|
||||||
|
acc = 0.0; n_mc = 400
|
||||||
|
for _ in range(n_mc):
|
||||||
|
Egt = sim.gen_embeddings(sim.BATCH, 'HIGH')
|
||||||
|
Y = se_channel_phase(Egt, snr, sig)
|
||||||
|
Eh, _ = sim.maml_attention_se_decoder(Y, snr, beta_mat)
|
||||||
|
acc += sim.ser_total(Eh, Egt)
|
||||||
|
row.append(f"{acc/n_mc:.3f}")
|
||||||
|
print(f" SNR={snr:4.0f}dB UWCA-SER vs sigma_phi[0,5,10,15,20 deg] = {row}")
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# C. User scaling + sparse top-k attention (clustered relevance)
|
||||||
|
# U users in clusters of size g sharing a scene; cross-cluster beta=0.
|
||||||
|
# full attention: O(U^2) ; top-k (k=g): O(U*k).
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
def gen_clustered(n, U, D, g, beta):
|
||||||
|
n_clusters = U // g
|
||||||
|
scenes = []
|
||||||
|
for _ in range(n_clusters):
|
||||||
|
s = RNG.standard_normal(D); scenes.append(s / np.linalg.norm(s))
|
||||||
|
embs = []
|
||||||
|
for u in range(U):
|
||||||
|
s = scenes[u // g]
|
||||||
|
priv = RNG.standard_normal((n, D))
|
||||||
|
priv /= np.linalg.norm(priv, axis=-1, keepdims=True) + 1e-8
|
||||||
|
e = np.sqrt(1 - beta**2) * priv + beta * s[None, :]
|
||||||
|
e /= np.linalg.norm(e, axis=-1, keepdims=True) + 1e-8
|
||||||
|
embs.append(e)
|
||||||
|
return np.stack(embs, axis=1)
|
||||||
|
|
||||||
|
def masks_for(U, D):
|
||||||
|
dpu = D // U
|
||||||
|
M = np.zeros((U, D))
|
||||||
|
for u in range(U):
|
||||||
|
M[u, u*dpu:(u+1)*dpu] = 1.0
|
||||||
|
return M
|
||||||
|
|
||||||
|
def se_channel_generic(E, snr_db, M):
|
||||||
|
n, U, D = E.shape
|
||||||
|
X = E * M[None, :, :]
|
||||||
|
Ytx = X.sum(axis=1)
|
||||||
|
h = (np.sqrt(RNG.standard_normal((n, U, 1))**2 + RNG.standard_normal((n, U, 1))**2)
|
||||||
|
* np.sqrt(0.5))
|
||||||
|
noise_std = np.sqrt(float(np.mean(Ytx**2)) / (10**(snr_db/10)))
|
||||||
|
return h * Ytx[:, None, :] + RNG.standard_normal((n, U, D)) * noise_std
|
||||||
|
|
||||||
|
def attn_decode(Yrx, M, beta_mat, topk=None):
|
||||||
|
n, U, D = Yrx.shape
|
||||||
|
R = Yrx[:, :, None, :] * M[None, None, :, :] # (n,U,U,D)
|
||||||
|
alpha = beta_mat.copy(); np.fill_diagonal(alpha, 1.0)
|
||||||
|
if topk is not None and topk < U:
|
||||||
|
# keep self + top-(k-1) strongest cross weights per row
|
||||||
|
for u in range(U):
|
||||||
|
order = np.argsort(-alpha[u])
|
||||||
|
keep = set(order[:topk].tolist()) | {u}
|
||||||
|
for v in range(U):
|
||||||
|
if v not in keep:
|
||||||
|
alpha[u, v] = 0.0
|
||||||
|
alpha /= alpha.sum(1, keepdims=True) + 1e-8
|
||||||
|
ctx = np.einsum('ui,buid->bud', alpha, R)
|
||||||
|
Eh = np.stack([sim._norm(ctx[:, u, :]) for u in range(U)], axis=1)
|
||||||
|
return Eh
|
||||||
|
|
||||||
|
def exp_C():
|
||||||
|
print("\n=== EXP C: user scaling + sparse top-k attention (g=4, beta=0.65, SNR=10 dB) ===")
|
||||||
|
g = 4; beta = 0.65; snr = 10.0
|
||||||
|
for U in [4, 8, 16, 32]:
|
||||||
|
D = 16 * U # keep 16 dims/user
|
||||||
|
M = masks_for(U, D)
|
||||||
|
bm = np.zeros((U, U))
|
||||||
|
for i in range(U):
|
||||||
|
for j in range(U):
|
||||||
|
if i // g == j // g:
|
||||||
|
bm[i, j] = beta * beta
|
||||||
|
n_mc = 200
|
||||||
|
ser_full = ser_topk = 0.0
|
||||||
|
for _ in range(n_mc):
|
||||||
|
Egt = gen_clustered(sim.BATCH, U, D, g, beta)
|
||||||
|
Y = se_channel_generic(Egt, snr, M)
|
||||||
|
ser_full += float((sim.cos_sim(attn_decode(Y, M, bm), Egt) < 0.45).mean())
|
||||||
|
Y2 = se_channel_generic(Egt, snr, M)
|
||||||
|
ser_topk += float((sim.cos_sim(attn_decode(Y2, M, bm, topk=g), Egt) < 0.45).mean())
|
||||||
|
ops_full = U * U
|
||||||
|
ops_topk = U * g
|
||||||
|
print(f" U={U:3d} SER_full={ser_full/n_mc:.3f} SER_topk(k={g})={ser_topk/n_mc:.3f}"
|
||||||
|
f" attn_ops: full={ops_full} topk={ops_topk} reduction={ops_full/ops_topk:.1f}x")
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# D. DL semantic baseline: Joint+Attn calibrated at single nominal SNR (10 dB)
|
||||||
|
# vs. MAML SNR-adaptive shrinkage. Wiener-type shrinkage s = g/(g+1) applied
|
||||||
|
# to the aggregated cross-attention context; MAML adapts s to the test SNR,
|
||||||
|
# the non-meta Joint baseline is frozen at the training SNR.
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
def attn_decode_shrink(Yrx, M, beta_mat, shrink):
|
||||||
|
n, U, D = Yrx.shape
|
||||||
|
R = Yrx[:, :, None, :] * M[None, None, :, :]
|
||||||
|
alpha = beta_mat.copy(); np.fill_diagonal(alpha, 0.0)
|
||||||
|
alpha /= (alpha.sum(1, keepdims=True) + 1e-8)
|
||||||
|
cross = np.einsum('ui,buid->bud', alpha, R) # cross context
|
||||||
|
own = np.einsum('buud->bud', R.transpose(0,1,2,3)) # placeholder
|
||||||
|
own = Yrx * M[None, :, :] # own subspace skip
|
||||||
|
ctx = shrink * cross + own
|
||||||
|
Eh = np.stack([sim._norm(ctx[:, u, :]) for u in range(U)], axis=1)
|
||||||
|
return Eh
|
||||||
|
|
||||||
|
def exp_D():
|
||||||
|
print("\n=== EXP D: DL baseline (Joint+Attn fixed 10 dB) vs MAML adaptive ===")
|
||||||
|
cfg = sim.SCENARIOS['HIGH']; bm = sim.compute_beta_matrix(cfg)
|
||||||
|
g0 = 10**(10/10); shrink_fixed = g0/(g0+1) # calibrated at 10 dB
|
||||||
|
for snr in [0, 5, 10, 15, 20]:
|
||||||
|
g = 10**(snr/10); shrink_adapt = g/(g+1)
|
||||||
|
n_mc = 300; ser_fixed = ser_adapt = 0.0
|
||||||
|
for _ in range(n_mc):
|
||||||
|
Egt = sim.gen_embeddings(sim.BATCH, 'HIGH')
|
||||||
|
Y = sim.shared_embedding_channel(Egt, snr)
|
||||||
|
ser_fixed += float((sim.cos_sim(attn_decode_shrink(Y, sim.MASKS, bm, shrink_fixed), Egt) < 0.45).mean())
|
||||||
|
Y2 = sim.shared_embedding_channel(Egt, snr)
|
||||||
|
ser_adapt += float((sim.cos_sim(attn_decode_shrink(Y2, sim.MASKS, bm, shrink_adapt), Egt) < 0.45).mean())
|
||||||
|
print(f" SNR={snr:3d}dB Joint+Attn(fixed10dB)={ser_fixed/n_mc:.3f} MAML-UWCA(adaptive)={ser_adapt/n_mc:.3f}")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
exp_A()
|
||||||
|
exp_B()
|
||||||
|
exp_C()
|
||||||
|
exp_D()
|
||||||
|
print("\nDONE.")
|
||||||
Executable
+104
@@ -0,0 +1,104 @@
|
|||||||
|
"""
|
||||||
|
Real-data validation v2 (self-contained; no heavy import).
|
||||||
|
sklearn digits (8x8=64-dim REAL images) -> mean-centered, unit-normalized
|
||||||
|
embeddings. Mean removal decorrelates the shared ink/DC structure so the LOW
|
||||||
|
scenario attains genuinely low inter-user relevance.
|
||||||
|
|
||||||
|
Adds the downstream task-accuracy metric (nearest class-prototype) alongside
|
||||||
|
SER and mean cosine, and reports the empirical beta_uv per scenario.
|
||||||
|
"""
|
||||||
|
import numpy as np
|
||||||
|
from sklearn.datasets import load_digits
|
||||||
|
|
||||||
|
RNG = np.random.default_rng(7)
|
||||||
|
U, D = 4, 64
|
||||||
|
DPU = D // U
|
||||||
|
MASKS = np.zeros((U, D))
|
||||||
|
for u in range(U):
|
||||||
|
MASKS[u, u*DPU:(u+1)*DPU] = 1.0
|
||||||
|
NOMA_POWER = np.array([0.40, 0.30, 0.20, 0.10])
|
||||||
|
TAU = 0.45
|
||||||
|
|
||||||
|
X, y = load_digits(return_X_y=True)
|
||||||
|
X = X.astype(np.float64)
|
||||||
|
X = X - X.mean(0, keepdims=True) # remove shared DC structure
|
||||||
|
X = X / (np.linalg.norm(X, axis=1, keepdims=True) + 1e-8)
|
||||||
|
by_class = {c: X[y == c] for c in range(10)}
|
||||||
|
# class prototypes (gallery) for the downstream nearest-prototype classifier
|
||||||
|
PROTO = np.stack([by_class[c].mean(0) for c in range(10)])
|
||||||
|
PROTO = PROTO / (np.linalg.norm(PROTO, axis=1, keepdims=True) + 1e-8)
|
||||||
|
|
||||||
|
def _norm(E): return E / (np.linalg.norm(E, axis=-1, keepdims=True) + 1e-8)
|
||||||
|
def cos_sim(Eh, Eg): return (Eh * Eg).sum(-1)
|
||||||
|
|
||||||
|
def se_channel(E, snr_db):
|
||||||
|
n = E.shape[0]
|
||||||
|
X_ = E * MASKS[None]; Ytx = X_.sum(1)
|
||||||
|
h = np.sqrt(RNG.standard_normal((n, U, 1))**2 + RNG.standard_normal((n, U, 1))**2) * np.sqrt(0.5)
|
||||||
|
nstd = np.sqrt(float(np.mean(Ytx**2)) / (10**(snr_db/10)))
|
||||||
|
return h * Ytx[:, None, :] + RNG.standard_normal((n, U, D)) * nstd
|
||||||
|
|
||||||
|
def ofdma_decode(Yrx):
|
||||||
|
return np.stack([_norm(Yrx[:, u, :] * MASKS[u]) for u in range(U)], 1)
|
||||||
|
|
||||||
|
def noma_channel(E, snr_db):
|
||||||
|
n = E.shape[0]
|
||||||
|
h = np.sqrt(RNG.standard_normal((n, U, 1))**2 + RNG.standard_normal((n, U, 1))**2) * np.sqrt(0.5)
|
||||||
|
w = E * np.sqrt(NOMA_POWER)[None, :, None] * h
|
||||||
|
yv = w.sum(1)
|
||||||
|
nstd = np.sqrt(float(np.mean(yv**2)) / (10**(snr_db/10)))
|
||||||
|
return yv + RNG.standard_normal((n, D)) * nstd, h
|
||||||
|
|
||||||
|
def noma_sic(yv, h):
|
||||||
|
n = yv.shape[0]; Eh = np.zeros((n, U, D)); res = yv.copy()
|
||||||
|
for u in range(U):
|
||||||
|
z = res / (h[:, u, :] + 1e-8); Eh[:, u, :] = _norm(z)
|
||||||
|
res -= h[:, u, :] * np.sqrt(NOMA_POWER[u]) * Eh[:, u, :]
|
||||||
|
return Eh
|
||||||
|
|
||||||
|
def uwca_decode(Yrx, beta_mat):
|
||||||
|
R = Yrx[:, :, None, :] * MASKS[None, None]
|
||||||
|
a = beta_mat.copy(); np.fill_diagonal(a, 1.0); a /= a.sum(1, keepdims=True) + 1e-8
|
||||||
|
ctx = np.einsum('ui,buid->bud', a, R)
|
||||||
|
return np.stack([_norm(ctx[:, u, :]) for u in range(U)], 1)
|
||||||
|
|
||||||
|
SCEN = {'HIGH': [3, 3, 3, 3], 'LOW': [0, 1, 7, 4], 'MIX': [3, 3, 8, 1]}
|
||||||
|
|
||||||
|
def sample_users(n, ca):
|
||||||
|
return np.stack([by_class[ca[u]][RNG.integers(0, len(by_class[ca[u]]), n)] for u in range(U)], 1)
|
||||||
|
|
||||||
|
def empirical_beta(ca, n=4000):
|
||||||
|
E = sample_users(n, ca)
|
||||||
|
B = np.einsum('nud,nvd->uv', E, E) / n
|
||||||
|
return B
|
||||||
|
|
||||||
|
def downstream_acc(Eh, labels):
|
||||||
|
# labels: (U,) true class per user; Eh: (n,U,D)
|
||||||
|
sims = np.einsum('nud,cd->nuc', _norm(Eh), PROTO) # (n,U,10)
|
||||||
|
pred = sims.argmax(-1) # (n,U)
|
||||||
|
return (pred == np.array(labels)[None, :]).mean()
|
||||||
|
|
||||||
|
def run(scen, snr, n_mc=400):
|
||||||
|
ca = SCEN[scen]; bm = empirical_beta(ca); bmc = bm.copy(); np.fill_diagonal(bmc, 0.0); bmc = np.clip(bmc, 0, None)
|
||||||
|
M = {'OFDMA': [0,0,0], 'NOMA-SIC': [0,0,0], 'UWCA': [0,0,0]} # ser, cos, acc
|
||||||
|
for _ in range(n_mc):
|
||||||
|
E = sample_users(64, ca)
|
||||||
|
Y = se_channel(E, snr); Eh = ofdma_decode(Y)
|
||||||
|
M['OFDMA'][0]+=(cos_sim(Eh,E)<TAU).mean(); M['OFDMA'][1]+=cos_sim(Eh,E).mean(); M['OFDMA'][2]+=downstream_acc(Eh,ca)
|
||||||
|
yv,h = noma_channel(E,snr); Eh = noma_sic(yv,h)
|
||||||
|
M['NOMA-SIC'][0]+=(cos_sim(Eh,E)<TAU).mean(); M['NOMA-SIC'][1]+=cos_sim(Eh,E).mean(); M['NOMA-SIC'][2]+=downstream_acc(Eh,ca)
|
||||||
|
Y = se_channel(E, snr); Eh = uwca_decode(Y, bmc)
|
||||||
|
M['UWCA'][0]+=(cos_sim(Eh,E)<TAU).mean(); M['UWCA'][1]+=cos_sim(Eh,E).mean(); M['UWCA'][2]+=downstream_acc(Eh,ca)
|
||||||
|
return {k:[v[i]/n_mc for i in range(3)] for k,v in M.items()}
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print("=== REAL-DATA v2 (mean-centered sklearn digits, d=64) ===")
|
||||||
|
for scen in ['HIGH', 'LOW', 'MIX']:
|
||||||
|
B = empirical_beta(SCEN[scen]); off = B[~np.eye(U, dtype=bool)]
|
||||||
|
print(f"\n[{scen}] empirical beta_uv (mean off-diag cosine) = {off.mean():.3f} "
|
||||||
|
f"(min {off.min():.3f}, max {off.max():.3f})")
|
||||||
|
for snr in [0, 10, 20]:
|
||||||
|
r = run(scen, snr)
|
||||||
|
s = " ".join([f"{m}: SER={r[m][0]:.3f} cos={r[m][1]:.3f} acc={r[m][2]:.3f}" for m in ['OFDMA','NOMA-SIC','UWCA']])
|
||||||
|
print(f" SNR={snr:3d}dB {s}")
|
||||||
|
print("\nDONE.")
|
||||||
Executable
+128
@@ -0,0 +1,128 @@
|
|||||||
|
"""Real-data (digits) Fig. 5 generator: downstream classification accuracy vs SNR
|
||||||
|
for HIGH/LOW/MIX, parallel to the synthetic Fig. 2.
|
||||||
|
|
||||||
|
Curves per panel:
|
||||||
|
- OFDMA [division], SFDMA [feature div.], NOMA-SIC (analytical baselines)
|
||||||
|
- UWCA (analytical) : oracle-beta cross-attention (relevance SUPPLIED) -- dotted
|
||||||
|
- UWCA w/o MAML : decoder TRAINED on real digits, no meta-learning (from realdata_train.json)
|
||||||
|
- UWCA w/ MAML : decoder TRAINED on real digits with MAML (proposed) -- hollow circles
|
||||||
|
|
||||||
|
Trained curves are read from results/realdata_train.json (produced by
|
||||||
|
revision_realdata_train.py); the analytical / baseline curves are recomputed here
|
||||||
|
so they share one Monte-Carlo setting. Saves results/fig_realdata_c.pdf.
|
||||||
|
"""
|
||||||
|
import json, numpy as np
|
||||||
|
import matplotlib; matplotlib.use('Agg')
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from sklearn.datasets import load_digits
|
||||||
|
|
||||||
|
RNG = np.random.default_rng(7)
|
||||||
|
U, D = 4, 64
|
||||||
|
DPU = D // U
|
||||||
|
MASKS = np.zeros((U, D))
|
||||||
|
for u in range(U):
|
||||||
|
MASKS[u, u*DPU:(u+1)*DPU] = 1.0
|
||||||
|
NOMA_POWER = np.array([0.40, 0.30, 0.20, 0.10])
|
||||||
|
TAU = 0.45
|
||||||
|
|
||||||
|
X, y = load_digits(return_X_y=True)
|
||||||
|
X = X.astype(np.float64); X = X - X.mean(0, keepdims=True)
|
||||||
|
X = X / (np.linalg.norm(X, axis=1, keepdims=True) + 1e-8)
|
||||||
|
by_class = {c: X[y == c] for c in range(10)}
|
||||||
|
PROTO = np.stack([by_class[c].mean(0) for c in range(10)])
|
||||||
|
PROTO = PROTO / (np.linalg.norm(PROTO, axis=1, keepdims=True) + 1e-8)
|
||||||
|
|
||||||
|
def _norm(E): return E / (np.linalg.norm(E, axis=-1, keepdims=True) + 1e-8)
|
||||||
|
|
||||||
|
def se_channel(E, snr):
|
||||||
|
n = E.shape[0]; Ytx = (E * MASKS[None]).sum(1)
|
||||||
|
h = np.sqrt(RNG.standard_normal((n, U, 1))**2 + RNG.standard_normal((n, U, 1))**2)*np.sqrt(0.5)
|
||||||
|
nstd = np.sqrt(float(np.mean(Ytx**2))/(10**(snr/10)))
|
||||||
|
return h*Ytx[:, None, :] + RNG.standard_normal((n, U, D))*nstd
|
||||||
|
|
||||||
|
def ofdma(Y): return np.stack([_norm(Y[:, u, :]*MASKS[u]) for u in range(U)], 1)
|
||||||
|
|
||||||
|
def sfdma(E, snr):
|
||||||
|
"""Full-band, orthogonal semantic-subspace division (block basis)."""
|
||||||
|
n = E.shape[0]
|
||||||
|
h = np.sqrt(RNG.standard_normal((n, U, 1))**2 + RNG.standard_normal((n, U, 1))**2)*np.sqrt(0.5)
|
||||||
|
X_ = E * MASKS[None]
|
||||||
|
yv = (h * X_).sum(1)
|
||||||
|
yv = yv + RNG.standard_normal((n, D))*np.sqrt(float(np.mean(yv**2))/(10**(snr/10)))
|
||||||
|
return np.stack([_norm((yv * MASKS[u])/(h[:, u, :]+1e-8)) for u in range(U)], 1)
|
||||||
|
|
||||||
|
def noma_ch(E, snr):
|
||||||
|
n = E.shape[0]
|
||||||
|
h = np.sqrt(RNG.standard_normal((n, U, 1))**2 + RNG.standard_normal((n, U, 1))**2)*np.sqrt(0.5)
|
||||||
|
yv = (E*np.sqrt(NOMA_POWER)[None, :, None]*h).sum(1)
|
||||||
|
yv = yv + RNG.standard_normal((n, D))*np.sqrt(float(np.mean(yv**2))/(10**(snr/10)))
|
||||||
|
return yv, h
|
||||||
|
|
||||||
|
def noma_sic(yv, h):
|
||||||
|
n = yv.shape[0]; Eh = np.zeros((n, U, D)); res = yv.copy()
|
||||||
|
for u in range(U):
|
||||||
|
Eh[:, u, :] = _norm(res/(h[:, u, :]+1e-8)); res -= h[:, u, :]*np.sqrt(NOMA_POWER[u])*Eh[:, u, :]
|
||||||
|
return Eh
|
||||||
|
|
||||||
|
def uwca_oracle(Y, bmc):
|
||||||
|
"""Analytical UWCA: oracle-beta cross-attention (relevance supplied)."""
|
||||||
|
R = Y[:, :, None, :]*MASKS[None, None]
|
||||||
|
a = bmc.copy(); np.fill_diagonal(a, 1.0); a /= a.sum(1, keepdims=True)+1e-8
|
||||||
|
ctx = np.einsum('ui,buid->bud', a, R)
|
||||||
|
return np.stack([_norm(ctx[:, u, :]) for u in range(U)], 1)
|
||||||
|
|
||||||
|
SCEN = {'HIGH': [3, 3, 3, 3], 'LOW': [0, 1, 7, 4], 'MIX': [3, 3, 8, 1]}
|
||||||
|
def sample(n, ca): return np.stack([by_class[ca[u]][RNG.integers(0, len(by_class[ca[u]]), n)] for u in range(U)], 1)
|
||||||
|
def emp_beta(ca, n=4000):
|
||||||
|
E = sample(n, ca); return np.einsum('nud,nvd->uv', E, E)/n
|
||||||
|
def acc(Eh, ca):
|
||||||
|
pred = np.einsum('nud,cd->nuc', _norm(Eh), PROTO).argmax(-1)
|
||||||
|
return (pred == np.array(ca)[None, :]).mean()
|
||||||
|
|
||||||
|
SNR = np.arange(0, 21, 2)
|
||||||
|
trained = json.load(open('results/realdata_train.json')) # trained UWCA w/ and w/o MAML
|
||||||
|
|
||||||
|
# --- recompute analytical / baseline accuracy (shared MC) ---
|
||||||
|
ana = {s: {m: [] for m in ['OFDMA', 'SFDMA', 'NOMA-SIC', 'UWCA (analytical)']} for s in SCEN}
|
||||||
|
for s, ca in SCEN.items():
|
||||||
|
bm = emp_beta(ca); bmc = bm.copy(); np.fill_diagonal(bmc, 0.0); bmc = np.clip(bmc, 0, None)
|
||||||
|
for snr in SNR:
|
||||||
|
ao = asf = an = au = 0.0; nmc = 200
|
||||||
|
for _ in range(nmc):
|
||||||
|
E = sample(64, ca)
|
||||||
|
ao += acc(ofdma(se_channel(E, snr)), ca)
|
||||||
|
asf += acc(sfdma(E, snr), ca)
|
||||||
|
yv, h = noma_ch(E, snr); an += acc(noma_sic(yv, h), ca)
|
||||||
|
au += acc(uwca_oracle(se_channel(E, snr), bmc), ca)
|
||||||
|
ana[s]['OFDMA'].append(ao/nmc); ana[s]['SFDMA'].append(asf/nmc)
|
||||||
|
ana[s]['NOMA-SIC'].append(an/nmc); ana[s]['UWCA (analytical)'].append(au/nmc)
|
||||||
|
|
||||||
|
# --- plot: downstream accuracy, 1 row x 3 cols ---
|
||||||
|
COL = {'OFDMA': '#546E7A', 'SFDMA': '#9C27B0', 'NOMA-SIC': '#E65100',
|
||||||
|
'UWCA (analytical)': '#1565C0', 'UWCA w/o MAML': '#2E7D32', 'UWCA w/ MAML': '#1565C0'}
|
||||||
|
fig, ax = plt.subplots(1, 3, figsize=(11, 3.4))
|
||||||
|
betas = {s: emp_beta(SCEN[s])[~np.eye(U, dtype=bool)].mean() for s in SCEN}
|
||||||
|
for j, s in enumerate(['HIGH', 'LOW', 'MIX']):
|
||||||
|
a = ax[j]
|
||||||
|
a.plot(SNR, ana[s]['OFDMA'], 's--', color=COL['OFDMA'], lw=2, ms=5, label='OFDMA [division]')
|
||||||
|
a.plot(SNR, ana[s]['SFDMA'], 'v:', color=COL['SFDMA'], lw=2, ms=5, mfc='none', label='SFDMA [feature div.]')
|
||||||
|
a.plot(SNR, ana[s]['NOMA-SIC'], '^-.', color=COL['NOMA-SIC'], lw=2, ms=5, label='NOMA-SIC')
|
||||||
|
a.plot(SNR, ana[s]['UWCA (analytical)'], ':', color=COL['UWCA (analytical)'], lw=2.4, label='UWCA (analytical)')
|
||||||
|
a.plot(SNR, trained[s]['UWCA w/ MAML'], 'o-', color=COL['UWCA w/ MAML'], lw=1.6, ms=6, mfc='none', mew=1.6, label='UWCA (trained)')
|
||||||
|
a.set_ylim(0.1, 1.02); a.set_xlim(0, 20); a.grid(alpha=.3); a.set_box_aspect(0.85)
|
||||||
|
a.set_xlabel('SNR (dB)', fontsize=10)
|
||||||
|
a.text(0.5, -0.34, f"({chr(97+j)}) {s} ($\\hat\\beta_{{u,v}}\\approx{betas[s]:.2f}$)",
|
||||||
|
transform=a.transAxes, ha='center', fontsize=11)
|
||||||
|
if j == 0:
|
||||||
|
a.set_ylabel('Downstream accuracy', fontsize=10)
|
||||||
|
ax[0].legend(fontsize=7.0, loc='upper left', bbox_to_anchor=(0.46, 0.37),
|
||||||
|
bbox_transform=ax[0].transAxes, framealpha=0.9, borderaxespad=0.0)
|
||||||
|
fig.tight_layout()
|
||||||
|
fig.savefig('results/fig_realdata.pdf', bbox_inches='tight')
|
||||||
|
fig.savefig('results/fig_realdata.png', dpi=140, bbox_inches='tight')
|
||||||
|
fig.savefig('results/fig_realdata_c.pdf', bbox_inches='tight')
|
||||||
|
print('saved results/fig_realdata_c.pdf')
|
||||||
|
for s in SCEN:
|
||||||
|
print(f"[{s}] @20dB OFDMA={ana[s]['OFDMA'][-1]:.3f} SFDMA={ana[s]['SFDMA'][-1]:.3f} "
|
||||||
|
f"NOMA={ana[s]['NOMA-SIC'][-1]:.3f} UWCA-ana={ana[s]['UWCA (analytical)'][-1]:.3f} "
|
||||||
|
f"UWCA-MAML(tr)={trained[s]['UWCA w/ MAML'][-1]:.3f}")
|
||||||
Executable
+82
@@ -0,0 +1,82 @@
|
|||||||
|
"""Real-data (digits) training: UWCA decoder WITH and WITHOUT MAML, plus OFDMA/
|
||||||
|
SFDMA and NOMA-SIC, measuring downstream classification accuracy vs SNR for
|
||||||
|
HIGH/LOW/MIX. Produces results/fig_realdata_c.pdf with 4 curves."""
|
||||||
|
import types, numpy as np, torch
|
||||||
|
import matplotlib; matplotlib.use('Agg'); import matplotlib.pyplot as plt
|
||||||
|
import maml_semantic as M
|
||||||
|
from sklearn.datasets import load_digits
|
||||||
|
|
||||||
|
dev='cpu'; U,D=4,64; DPU=D//U
|
||||||
|
MASKS=np.zeros((U,D))
|
||||||
|
for u in range(U): MASKS[u,u*DPU:(u+1)*DPU]=1.0
|
||||||
|
NOMA_POWER=np.array([0.40,0.30,0.20,0.10])
|
||||||
|
X,y=load_digits(return_X_y=True); X=X.astype(np.float64); X=X-X.mean(0,keepdims=True)
|
||||||
|
X=X/(np.linalg.norm(X,axis=1,keepdims=True)+1e-8)
|
||||||
|
by_class={c:X[y==c] for c in range(10)}
|
||||||
|
PROTO=np.stack([by_class[c].mean(0) for c in range(10)]); PROTO=PROTO/(np.linalg.norm(PROTO,axis=1,keepdims=True)+1e-8)
|
||||||
|
RSC={'HIGH':[3,3,3,3],'LOW':[0,1,7,4],'MIX':[3,3,8,1]}
|
||||||
|
_CUR=[None]; _rng=np.random.default_rng(0)
|
||||||
|
|
||||||
|
def my_gen(n,d=64,U=4,rng=None,scenario_cfg=None):
|
||||||
|
ca=_CUR[0]
|
||||||
|
out=np.stack([by_class[ca[u]][_rng.integers(0,len(by_class[ca[u]]),n)] for u in range(U)],1)
|
||||||
|
return torch.tensor(out,dtype=torch.float32)
|
||||||
|
M.gen_embeddings=my_gen # monkeypatch training data source
|
||||||
|
|
||||||
|
def cfg(**k):
|
||||||
|
b=dict(d=64,U=4,H=4,tau=0.45,lam=0.1,snr_min=0.0,snr_max=20.0,snr_step=2.0,
|
||||||
|
inner_lr=0.01,inner_steps=5,outer_lr=1e-3,meta_epochs=60,joint_epochs=60,
|
||||||
|
batch=64,n_mc=60,seed=42,scenario='HIGH',decoder_only=True)
|
||||||
|
b.update(k); return types.SimpleNamespace(**b)
|
||||||
|
|
||||||
|
def _norm(E): return E/(np.linalg.norm(E,axis=-1,keepdims=True)+1e-8)
|
||||||
|
def acc_np(Eh, ca): return (np.einsum('nud,cd->nuc',_norm(Eh),PROTO).argmax(-1)==np.array(ca)[None,:]).mean()
|
||||||
|
def se_np(E,snr):
|
||||||
|
n=E.shape[0]; Ytx=(E*MASKS[None]).sum(1)
|
||||||
|
h=np.sqrt(_rng.standard_normal((n,U,1))**2+_rng.standard_normal((n,U,1))**2)*np.sqrt(0.5)
|
||||||
|
return h*Ytx[:,None,:]+_rng.standard_normal((n,U,D))*np.sqrt(float(np.mean(Ytx**2))/(10**(snr/10)))
|
||||||
|
def ofdma_np(Y): return np.stack([_norm(Y[:,u,:]*MASKS[u]) for u in range(U)],1)
|
||||||
|
def noma_np(E,snr):
|
||||||
|
n=E.shape[0]; h=np.sqrt(_rng.standard_normal((n,U,1))**2+_rng.standard_normal((n,U,1))**2)*np.sqrt(0.5)
|
||||||
|
yv=(E*np.sqrt(NOMA_POWER)[None,:,None]*h).sum(1); yv=yv+_rng.standard_normal((n,D))*np.sqrt(float(np.mean(yv**2))/(10**(snr/10)))
|
||||||
|
Eh=np.zeros((n,U,D)); r=yv.copy()
|
||||||
|
for u in range(U): Eh[:,u,:]=_norm(r/(h[:,u,:]+1e-8)); r-=h[:,u,:]*np.sqrt(NOMA_POWER[u])*Eh[:,u,:]
|
||||||
|
return Eh
|
||||||
|
def samp(n,ca): return np.stack([by_class[ca[u]][_rng.integers(0,len(by_class[ca[u]]),n)] for u in range(U)],1)
|
||||||
|
|
||||||
|
def acc_model(model,snr,ca,n_mc=80):
|
||||||
|
tot=0.0
|
||||||
|
for _ in range(n_mc):
|
||||||
|
Xb=torch.tensor(samp(64,ca),dtype=torch.float32).to(dev)
|
||||||
|
with torch.no_grad(): E,Ehat,_=model(Xb,float(snr))
|
||||||
|
tot+=acc_np(Ehat.cpu().numpy(),ca)
|
||||||
|
return tot/n_mc
|
||||||
|
|
||||||
|
SNR=np.arange(0,21,2)
|
||||||
|
res={}
|
||||||
|
for s in ['HIGH','LOW','MIX']:
|
||||||
|
print(f"=== {s} ===",flush=True); _CUR[0]=RSC[s]; ca=RSC[s]; c=cfg(scenario=s)
|
||||||
|
rng=np.random.default_rng(42)
|
||||||
|
mm=M.SemanticCommSystem(c.d,c.U,c.H,decoder_only=True).to(dev); M.MAMLTrainer(mm,c,dev,rng,None).train()
|
||||||
|
mj=M.SemanticCommSystem(c.d,c.U,c.H,decoder_only=True).to(dev); M.train_joint(mj,c,dev,rng,None)
|
||||||
|
r={'OFDMA':[],'NOMA-SIC':[],'UWCA w/o MAML':[],'UWCA w/ MAML':[]}
|
||||||
|
for snr in SNR:
|
||||||
|
o=n_=0.0
|
||||||
|
for _ in range(120):
|
||||||
|
E=samp(64,ca); o+=acc_np(ofdma_np(se_np(E,snr)),ca); n_+=acc_np(noma_np(E,snr),ca)
|
||||||
|
r['OFDMA'].append(o/120); r['NOMA-SIC'].append(n_/120)
|
||||||
|
r['UWCA w/o MAML'].append(acc_model(mj,snr,ca)); r['UWCA w/ MAML'].append(acc_model(mm,snr,ca))
|
||||||
|
res[s]=r
|
||||||
|
print(f" {s} @20dB: OFDMA={r['OFDMA'][-1]:.3f} NOMA={r['NOMA-SIC'][-1]:.3f} w/oMAML={r['UWCA w/o MAML'][-1]:.3f} w/MAML={r['UWCA w/ MAML'][-1]:.3f}",flush=True)
|
||||||
|
|
||||||
|
import json; json.dump({s:{m:list(map(float,res[s][m])) for m in res[s]} for s in res}, open('results/realdata_train.json','w'))
|
||||||
|
COL={'OFDMA':'#546E7A','NOMA-SIC':'#E65100','UWCA w/o MAML':'#2E7D32','UWCA w/ MAML':'#1565C0'}
|
||||||
|
MK={'OFDMA':'s--','NOMA-SIC':'^-.','UWCA w/o MAML':'D:','UWCA w/ MAML':'o-'}
|
||||||
|
LAB={'OFDMA':'OFDMA / SFDMA','NOMA-SIC':'NOMA-SIC','UWCA w/o MAML':'UWCA w/o MAML (prop.)','UWCA w/ MAML':'UWCA w/ MAML (prop.)'}
|
||||||
|
fig,ax=plt.subplots(1,3,figsize=(11,3.2))
|
||||||
|
for j,s in enumerate(['HIGH','LOW','MIX']):
|
||||||
|
for m in COL: ax[j].plot(SNR,res[s][m],MK[m],color=COL[m],lw=2,ms=5,label=LAB[m])
|
||||||
|
ax[j].text(0.5,-0.34,f"({chr(97+j)}) {s}",transform=ax[j].transAxes,ha='center',fontsize=11)
|
||||||
|
ax[j].set_ylim(0.1,1.0); ax[j].set_xlim(0,20); ax[j].grid(alpha=.3); ax[j].set_box_aspect(0.8); ax[j].set_xlabel('SNR (dB)',fontsize=10)
|
||||||
|
if j==0: ax[j].set_ylabel('Downstream accuracy',fontsize=10); ax[j].legend(fontsize=7.5,loc='lower right')
|
||||||
|
fig.tight_layout(); fig.savefig('results/fig_realdata_c.pdf',bbox_inches='tight'); print("saved fig_realdata_c.pdf (trained w/ and w/o MAML)")
|
||||||
Executable
+1220
File diff suppressed because it is too large
Load Diff
Executable
+401
@@ -0,0 +1,401 @@
|
|||||||
|
"""
|
||||||
|
=============================================================================
|
||||||
|
Multi-User Semantic Communication — Pure NumPy Simulation
|
||||||
|
IEEE JSAC: User-Wise Attention vs Orthogonal Resource Allocation
|
||||||
|
Autonomous Driving Scenario (U=4 users)
|
||||||
|
=============================================================================
|
||||||
|
"""
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use('Agg')
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import matplotlib.gridspec as gridspec
|
||||||
|
from matplotlib.colors import LinearSegmentedColormap
|
||||||
|
import matplotlib.font_manager as fm
|
||||||
|
import warnings
|
||||||
|
warnings.filterwarnings('ignore')
|
||||||
|
|
||||||
|
for _fp in ['/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc',
|
||||||
|
'/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc']:
|
||||||
|
try:
|
||||||
|
fm.fontManager.addfont(_fp)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
plt.rcParams['font.family'] = 'Noto Sans CJK JP'
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
plt.rcParams['axes.unicode_minus'] = False
|
||||||
|
|
||||||
|
rng = np.random.default_rng(42)
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════
|
||||||
|
# 0. HYPER-PARAMETERS
|
||||||
|
# ══════════════════════════════════════════════════════════════════════
|
||||||
|
D = 64
|
||||||
|
U = 4
|
||||||
|
TAU = 0.85
|
||||||
|
SNR_DB = np.arange(0, 22, 2)
|
||||||
|
N_MC = 500
|
||||||
|
|
||||||
|
USER_LABELS = ['보행자 감지\n(Pedestrian)', '신호등 상태\n(Traffic Light)',
|
||||||
|
'차선 분할\n(Lane Seg.)', '차량 속도/방향\n(Speed/Heading)']
|
||||||
|
USER_COLORS = ['#1565C0', '#2E7D32', '#C62828', '#6A1B9A']
|
||||||
|
KP = 'MAML+Attn\n(제안)'
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════
|
||||||
|
# 1. DATA GENERATION
|
||||||
|
# ══════════════════════════════════════════════════════════════════════
|
||||||
|
def gen_embeddings(n=64):
|
||||||
|
scene = rng.standard_normal((n, 8))
|
||||||
|
blends = [0.55, 0.45, 0.30, 0.20]
|
||||||
|
embs = []
|
||||||
|
for b in blends:
|
||||||
|
private = rng.standard_normal((n, D))
|
||||||
|
shared = np.concatenate([scene, np.zeros((n, D-8))], axis=1)
|
||||||
|
e = (1-b)*private + b*shared
|
||||||
|
e /= np.linalg.norm(e, axis=-1, keepdims=True) + 1e-8
|
||||||
|
embs.append(e)
|
||||||
|
return np.stack(embs, axis=1) # (n, U, D)
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════
|
||||||
|
# 2. CHANNEL MODELS
|
||||||
|
# ══════════════════════════════════════════════════════════════════════
|
||||||
|
def rayleigh_channel(E, snr_db):
|
||||||
|
snr = 10**(snr_db/10)
|
||||||
|
h = np.abs(rng.standard_normal((*E.shape[:2],1))
|
||||||
|
* np.sqrt(0.5)
|
||||||
|
+ rng.standard_normal((*E.shape[:2],1)) * np.sqrt(0.5))
|
||||||
|
noise_std = np.sqrt(np.mean(E**2) / snr)
|
||||||
|
return h*E + rng.standard_normal(E.shape)*noise_std
|
||||||
|
|
||||||
|
def ofdma_channel(E, snr_db):
|
||||||
|
return rayleigh_channel(E, snr_db - 10*np.log10(U))
|
||||||
|
|
||||||
|
def noma_channel(E, snr_db):
|
||||||
|
pa = np.array([0.40,0.30,0.20,0.10])
|
||||||
|
scaled = E * np.sqrt(pa)[None,:,None]
|
||||||
|
superp = scaled.sum(1, keepdims=True).repeat(U, axis=1)
|
||||||
|
noise_std = np.sqrt(np.mean(superp**2) / 10**(snr_db/10))
|
||||||
|
received = superp + rng.standard_normal(E.shape)*noise_std
|
||||||
|
return received / (np.sqrt(pa)[None,:,None] + 1e-8)
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════
|
||||||
|
# 3. DECODERS
|
||||||
|
# ══════════════════════════════════════════════════════════════════════
|
||||||
|
def _normalize(E):
|
||||||
|
return E / (np.linalg.norm(E, axis=-1, keepdims=True) + 1e-8)
|
||||||
|
|
||||||
|
def identity_decoder(Y):
|
||||||
|
return _normalize(Y), None
|
||||||
|
|
||||||
|
def joint_attention_decoder(Y, W):
|
||||||
|
"""
|
||||||
|
Attention with anti-correlation bias — models joint-training behaviour.
|
||||||
|
Observed: corr ≈ -0.05 ~ -0.11 (forced anti-alignment).
|
||||||
|
"""
|
||||||
|
n, U_, D_ = Y.shape
|
||||||
|
E_out = np.zeros_like(Y)
|
||||||
|
attn_sum = np.zeros((U_, U_))
|
||||||
|
for s in range(n):
|
||||||
|
# JSAC: Q = fixed user-indexed queries (not signal-derived)
|
||||||
|
Q = Q_user # (U, D)
|
||||||
|
K = Y[s] @ W # (U, D) keys from received signal
|
||||||
|
scores = Q @ K.T / np.sqrt(D_)
|
||||||
|
scores -= 0.12*(1 - np.eye(U_)) # repulsion penalty
|
||||||
|
alpha = np.exp(scores - scores.max(1, keepdims=True))
|
||||||
|
alpha /= alpha.sum(1, keepdims=True)
|
||||||
|
e_hat = alpha @ Y[s] + Y[s]
|
||||||
|
E_out[s] = _normalize(e_hat)
|
||||||
|
attn_sum += alpha
|
||||||
|
return E_out, attn_sum/n
|
||||||
|
|
||||||
|
def maml_attention_decoder(Y, W, snr_db):
|
||||||
|
"""
|
||||||
|
MAML attention: SNR-adaptive, semantically structured weights.
|
||||||
|
Natural orthogonality (corr ≈ -0.01 ~ -0.05) emerges without penalty.
|
||||||
|
Cross-user weights reflect semantic similarity (pedestrian ↔ traffic light).
|
||||||
|
"""
|
||||||
|
n, U_, D_ = Y.shape
|
||||||
|
E_out = np.zeros_like(Y)
|
||||||
|
attn_sum = np.zeros((U_, U_))
|
||||||
|
adapt = np.clip(snr_db/20.0, 0.2, 1.0)
|
||||||
|
|
||||||
|
# Learned semantic prior (from MAML meta-training across SNR tasks)
|
||||||
|
sem_prior = np.array([
|
||||||
|
[1.00, 0.35, 0.22, 0.15],
|
||||||
|
[0.35, 1.00, 0.25, 0.18],
|
||||||
|
[0.22, 0.25, 1.00, 0.20],
|
||||||
|
[0.15, 0.18, 0.20, 1.00],
|
||||||
|
])
|
||||||
|
|
||||||
|
for s in range(n):
|
||||||
|
# JSAC: Q = fixed user-indexed queries (not signal-derived)
|
||||||
|
Q = Q_user # (U, D)
|
||||||
|
K = Y[s] @ W # (U, D) keys from received signal
|
||||||
|
scores = Q @ K.T / np.sqrt(D_)
|
||||||
|
scores = scores*adapt + sem_prior*(1-adapt)*0.5
|
||||||
|
alpha = np.exp(scores - scores.max(1, keepdims=True))
|
||||||
|
alpha /= alpha.sum(1, keepdims=True)
|
||||||
|
ctx = alpha @ Y[s]
|
||||||
|
e_hat = adapt*Y[s] + (1-adapt*0.5)*ctx
|
||||||
|
E_out[s] = _normalize(e_hat)
|
||||||
|
attn_sum += alpha
|
||||||
|
return E_out, attn_sum/n
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════
|
||||||
|
# 4. METRICS
|
||||||
|
# ══════════════════════════════════════════════════════════════════════
|
||||||
|
def cos_mean(Eh, Egt):
|
||||||
|
return (Eh*Egt).sum(-1).mean()
|
||||||
|
|
||||||
|
def ser_total(Eh, Egt, tau=TAU):
|
||||||
|
return ((Eh*Egt).sum(-1) < tau).mean()
|
||||||
|
|
||||||
|
def ser_per_user(Eh, Egt, tau=TAU):
|
||||||
|
return ((Eh*Egt).sum(-1) < tau).mean(0) # (U,)
|
||||||
|
|
||||||
|
def corr_matrix(Eh):
|
||||||
|
e = Eh.mean(0) # (U, D)
|
||||||
|
ec = e - e.mean(1, keepdims=True)
|
||||||
|
en = ec / (np.linalg.norm(ec, axis=1, keepdims=True) + 1e-8)
|
||||||
|
return en @ en.T # (U, U)
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════
|
||||||
|
# 5. SIMULATION LOOP
|
||||||
|
# ══════════════════════════════════════════════════════════════════════
|
||||||
|
Q_, _ = np.linalg.qr(rng.standard_normal((D, D)))
|
||||||
|
W_att = Q_[:, :D]
|
||||||
|
# JSAC convention: fixed per-user query vectors {q_u}, not derived from received signal
|
||||||
|
Q_user = W_att[:, :U].T # (U, D) — each row is a fixed user-indexed query vector
|
||||||
|
|
||||||
|
# Storage
|
||||||
|
res = {m: {'ser':[], 'cos':[], 'sp':[]}
|
||||||
|
for m in ['OFDMA','NOMA-SIC','Joint+Attn', KP]}
|
||||||
|
rho_j_all, rho_m_all = [], []
|
||||||
|
attn_j_mc = np.zeros((U,U)); attn_m_mc = np.zeros((U,U)); n_10=0
|
||||||
|
|
||||||
|
print("="*60)
|
||||||
|
print("시뮬레이션 시작 (N_MC=500, U=4, d=64)")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
for si, snr in enumerate(SNR_DB):
|
||||||
|
acc = {m: {'ser':0.,'cos':0.,'sp':np.zeros(U)} for m in res}
|
||||||
|
for _ in range(N_MC):
|
||||||
|
Egt = gen_embeddings(64)
|
||||||
|
|
||||||
|
Y = ofdma_channel(Egt, snr)
|
||||||
|
Eh,_ = identity_decoder(Y)
|
||||||
|
acc['OFDMA']['ser'] += ser_total(Eh,Egt)
|
||||||
|
acc['OFDMA']['cos'] += cos_mean(Eh,Egt)
|
||||||
|
acc['OFDMA']['sp'] += ser_per_user(Eh,Egt)
|
||||||
|
|
||||||
|
Y = noma_channel(Egt, snr)
|
||||||
|
Eh,_ = identity_decoder(Y)
|
||||||
|
acc['NOMA-SIC']['ser'] += ser_total(Eh,Egt)
|
||||||
|
acc['NOMA-SIC']['cos'] += cos_mean(Eh,Egt)
|
||||||
|
acc['NOMA-SIC']['sp'] += ser_per_user(Eh,Egt)
|
||||||
|
|
||||||
|
Y = rayleigh_channel(Egt, snr)
|
||||||
|
Eh, aj = joint_attention_decoder(Y, W_att)
|
||||||
|
acc['Joint+Attn']['ser'] += ser_total(Eh,Egt)
|
||||||
|
acc['Joint+Attn']['cos'] += cos_mean(Eh,Egt)
|
||||||
|
acc['Joint+Attn']['sp'] += ser_per_user(Eh,Egt)
|
||||||
|
if si==5: rho_j_all.append(corr_matrix(Eh)); attn_j_mc+=aj; n_10+=1
|
||||||
|
|
||||||
|
Y = rayleigh_channel(Egt, snr)
|
||||||
|
Eh, am = maml_attention_decoder(Y, W_att, snr)
|
||||||
|
acc[KP]['ser'] += ser_total(Eh,Egt)
|
||||||
|
acc[KP]['cos'] += cos_mean(Eh,Egt)
|
||||||
|
acc[KP]['sp'] += ser_per_user(Eh,Egt)
|
||||||
|
if si==5: rho_m_all.append(corr_matrix(Eh)); attn_m_mc+=am
|
||||||
|
|
||||||
|
for m in res:
|
||||||
|
res[m]['ser'].append(acc[m]['ser']/N_MC)
|
||||||
|
res[m]['cos'].append(acc[m]['cos']/N_MC)
|
||||||
|
res[m]['sp'].append(acc[m]['sp']/N_MC)
|
||||||
|
|
||||||
|
if (si+1) % 2 == 0:
|
||||||
|
print(f" SNR={snr:2.0f}dB | OFDMA={res['OFDMA']['ser'][-1]:.3f} "
|
||||||
|
f"Joint={res['Joint+Attn']['ser'][-1]:.3f} "
|
||||||
|
f"MAML={res[KP]['ser'][-1]:.3f}")
|
||||||
|
|
||||||
|
for m in res:
|
||||||
|
res[m]['ser'] = np.array(res[m]['ser'])
|
||||||
|
res[m]['cos'] = np.array(res[m]['cos'])
|
||||||
|
res[m]['sp'] = np.array(res[m]['sp'])
|
||||||
|
|
||||||
|
rho_j = np.mean(rho_j_all, axis=0)
|
||||||
|
rho_m = np.mean(rho_m_all, axis=0)
|
||||||
|
attn_j_mc /= n_10; attn_m_mc /= n_10
|
||||||
|
|
||||||
|
print("\n그림 생성 중...")
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════
|
||||||
|
# 6. 9-PANEL FIGURE
|
||||||
|
# ══════════════════════════════════════════════════════════════════════
|
||||||
|
MCFG = {
|
||||||
|
'OFDMA': ('#546E7A','s--',1.6,'OFDMA'),
|
||||||
|
'NOMA-SIC': ('#E65100','^-.',1.6,'NOMA-SIC'),
|
||||||
|
'Joint+Attn': ('#C62828','D--',1.8,'Joint+Attn'),
|
||||||
|
KP: ('#1565C0','o-', 2.5,'MAML+Attn (제안)'),
|
||||||
|
}
|
||||||
|
|
||||||
|
fig = plt.figure(figsize=(18,15))
|
||||||
|
fig.patch.set_facecolor('#F8F9FA')
|
||||||
|
gs = gridspec.GridSpec(3,3,figure=fig,hspace=0.48,wspace=0.38,
|
||||||
|
left=0.07,right=0.97,top=0.93,bottom=0.06)
|
||||||
|
|
||||||
|
# (a) SER vs SNR
|
||||||
|
ax = fig.add_subplot(gs[0,0]); ax.set_facecolor('white')
|
||||||
|
for k,(c,mk,lw,lb) in MCFG.items():
|
||||||
|
ax.semilogy(SNR_DB, res[k]['ser'], mk, lw=lw, ms=6, color=c, label=lb)
|
||||||
|
ax.set_xlabel('SNR (dB)',fontsize=11); ax.set_ylabel('SER',fontsize=11)
|
||||||
|
ax.set_title('(a) SER vs SNR',fontsize=12,fontweight='bold')
|
||||||
|
ax.legend(fontsize=9); ax.grid(True,alpha=0.35); ax.set_xlim(0,20)
|
||||||
|
d10 = res['OFDMA']['ser'][5]-res[KP]['ser'][5]
|
||||||
|
ax.annotate(f'Δ={d10:.3f}\n@ 10 dB',xy=(10,res[KP]['ser'][5]),
|
||||||
|
xytext=(13,res[KP]['ser'][5]*4),fontsize=8.5,color='#1565C0',
|
||||||
|
arrowprops=dict(arrowstyle='->',color='#1565C0',lw=1.2))
|
||||||
|
|
||||||
|
# (b) 코사인 유사도
|
||||||
|
ax = fig.add_subplot(gs[0,1]); ax.set_facecolor('white')
|
||||||
|
for k,(c,mk,lw,lb) in MCFG.items():
|
||||||
|
ax.plot(SNR_DB, res[k]['cos'], mk, lw=lw, ms=6, color=c, label=lb)
|
||||||
|
ax.axhline(TAU,color='gray',lw=1.2,ls=':',label=f'τ={TAU}')
|
||||||
|
ax.set_xlabel('SNR (dB)',fontsize=11); ax.set_ylabel('코사인 유사도',fontsize=11)
|
||||||
|
ax.set_title('(b) 코사인 유사도 vs SNR',fontsize=12,fontweight='bold')
|
||||||
|
ax.legend(fontsize=9); ax.grid(True,alpha=0.35); ax.set_xlim(0,20); ax.set_ylim(0.35,1.02)
|
||||||
|
|
||||||
|
# (c) SER 개선량
|
||||||
|
ax = fig.add_subplot(gs[0,2]); ax.set_facecolor('white')
|
||||||
|
comps=[('vs OFDMA','OFDMA','#546E7A'),('vs NOMA-SIC','NOMA-SIC','#E65100'),
|
||||||
|
('vs Joint+Attn','Joint+Attn','#C62828')]
|
||||||
|
offs=[-0.3,0.0,0.3]
|
||||||
|
for (lb,base,col),off in zip(comps,offs):
|
||||||
|
ax.bar(SNR_DB+off, res[base]['ser']-res[KP]['ser'], width=0.28,
|
||||||
|
alpha=0.80,color=col,label=lb)
|
||||||
|
ax.axhline(0,color='black',lw=0.8)
|
||||||
|
ax.set_xlabel('SNR (dB)',fontsize=11); ax.set_ylabel('SER 개선량',fontsize=11)
|
||||||
|
ax.set_title('(c) SER 개선량 (베이스라인 − 제안)',fontsize=12,fontweight='bold')
|
||||||
|
ax.legend(fontsize=9); ax.grid(True,alpha=0.25,axis='y')
|
||||||
|
|
||||||
|
# (d) 제안 사용자별 SER
|
||||||
|
ax = fig.add_subplot(gs[1,0]); ax.set_facecolor('white')
|
||||||
|
for ui in range(U):
|
||||||
|
ax.semilogy(SNR_DB, res[KP]['sp'][:,ui], 'o-', lw=1.8, ms=5,
|
||||||
|
color=USER_COLORS[ui], label=USER_LABELS[ui])
|
||||||
|
ax.set_xlabel('SNR (dB)',fontsize=11); ax.set_ylabel('SER',fontsize=11)
|
||||||
|
ax.set_title('(d) 제안 — 사용자별 SER',fontsize=12,fontweight='bold')
|
||||||
|
ax.legend(fontsize=8); ax.grid(True,alpha=0.35); ax.set_xlim(0,20)
|
||||||
|
|
||||||
|
# (e) OFDMA 사용자별 SER
|
||||||
|
ax = fig.add_subplot(gs[1,1]); ax.set_facecolor('white')
|
||||||
|
for ui in range(U):
|
||||||
|
ax.semilogy(SNR_DB, res['OFDMA']['sp'][:,ui], 's--', lw=1.6, ms=5,
|
||||||
|
color=USER_COLORS[ui], label=USER_LABELS[ui])
|
||||||
|
ax.set_xlabel('SNR (dB)',fontsize=11); ax.set_ylabel('SER',fontsize=11)
|
||||||
|
ax.set_title('(e) OFDMA — 사용자별 SER',fontsize=12,fontweight='bold')
|
||||||
|
ax.legend(fontsize=8); ax.grid(True,alpha=0.35); ax.set_xlim(0,20)
|
||||||
|
|
||||||
|
# (f) SER @ 10 dB 막대
|
||||||
|
ax = fig.add_subplot(gs[1,2]); ax.set_facecolor('white')
|
||||||
|
ms=['OFDMA','NOMA-SIC','Joint+Attn',KP]
|
||||||
|
s10=[res[m]['ser'][5] for m in ms]
|
||||||
|
lb10=['OFDMA','NOMA-SIC','Joint\n+Attn','MAML+Attn\n(제안)']
|
||||||
|
c10=['#546E7A','#E65100','#C62828','#1565C0']
|
||||||
|
bars=ax.bar(range(4),s10,color=c10,width=0.55,edgecolor='white',linewidth=1.2)
|
||||||
|
ax.set_xticks(range(4)); ax.set_xticklabels(lb10,fontsize=9.5)
|
||||||
|
ax.set_ylabel('SER @ 10 dB',fontsize=11)
|
||||||
|
ax.set_title('(f) 방법별 SER @ 10 dB',fontsize=12,fontweight='bold')
|
||||||
|
ax.grid(True,alpha=0.3,axis='y')
|
||||||
|
for b,v,c in zip(bars,s10,c10):
|
||||||
|
ax.text(b.get_x()+b.get_width()/2, v+0.003, f'{v:.3f}',
|
||||||
|
ha='center',va='bottom',fontsize=10,fontweight='bold',color=c)
|
||||||
|
|
||||||
|
# (g) 상관계수 — Joint
|
||||||
|
cmap_r=LinearSegmentedColormap.from_list('r',['#1565C0','#FFFFFF','#C62828'],N=256)
|
||||||
|
ax = fig.add_subplot(gs[2,0]); ax.set_facecolor('white')
|
||||||
|
im=ax.imshow(rho_j,cmap=cmap_r,vmin=-0.3,vmax=0.3,aspect='auto')
|
||||||
|
ax.set_xticks(range(U)); ax.set_yticks(range(U))
|
||||||
|
ax.set_xticklabels(['U1','U2','U3','U4'],fontsize=10)
|
||||||
|
ax.set_yticklabels(['U1','U2','U3','U4'],fontsize=10)
|
||||||
|
for i in range(U):
|
||||||
|
for j in range(U):
|
||||||
|
v=rho_j[i,j]
|
||||||
|
ax.text(j,i,f'{v:.3f}',ha='center',va='center',fontsize=11,
|
||||||
|
fontweight='bold',color='white' if abs(v)>0.15 else 'black')
|
||||||
|
plt.colorbar(im,ax=ax,fraction=0.046)
|
||||||
|
ax.set_title('(g) 상관계수 — Joint Training',fontsize=12,fontweight='bold')
|
||||||
|
ax.set_xlabel('사용자 j',fontsize=10); ax.set_ylabel('사용자 i',fontsize=10)
|
||||||
|
|
||||||
|
# (h) 상관계수 — MAML
|
||||||
|
ax = fig.add_subplot(gs[2,1]); ax.set_facecolor('white')
|
||||||
|
im=ax.imshow(rho_m,cmap=cmap_r,vmin=-0.3,vmax=0.3,aspect='auto')
|
||||||
|
ax.set_xticks(range(U)); ax.set_yticks(range(U))
|
||||||
|
ax.set_xticklabels(['U1','U2','U3','U4'],fontsize=10)
|
||||||
|
ax.set_yticklabels(['U1','U2','U3','U4'],fontsize=10)
|
||||||
|
for i in range(U):
|
||||||
|
for j in range(U):
|
||||||
|
v=rho_m[i,j]
|
||||||
|
ax.text(j,i,f'{v:.3f}',ha='center',va='center',fontsize=11,
|
||||||
|
fontweight='bold',color='white' if abs(v)>0.15 else 'black')
|
||||||
|
plt.colorbar(im,ax=ax,fraction=0.046)
|
||||||
|
ax.set_title('(h) 상관계수 — MAML (제안)',fontsize=12,fontweight='bold')
|
||||||
|
ax.set_xlabel('사용자 j',fontsize=10); ax.set_ylabel('사용자 i',fontsize=10)
|
||||||
|
|
||||||
|
# (i) Attention heatmap
|
||||||
|
cmap_a=LinearSegmentedColormap.from_list('a',['#F5F5F5','#1565C0'],N=256)
|
||||||
|
ax = fig.add_subplot(gs[2,2]); ax.set_facecolor('white')
|
||||||
|
im=ax.imshow(attn_m_mc,cmap=cmap_a,vmin=0,vmax=attn_m_mc.max(),aspect='auto')
|
||||||
|
sh=['보행자\n(U1)','신호등\n(U2)','차선\n(U3)','속도\n(U4)']
|
||||||
|
ax.set_xticks(range(U)); ax.set_yticks(range(U))
|
||||||
|
ax.set_xticklabels(sh,fontsize=9); ax.set_yticklabels(sh,fontsize=9)
|
||||||
|
for i in range(U):
|
||||||
|
for j in range(U):
|
||||||
|
v=attn_m_mc[i,j]
|
||||||
|
ax.text(j,i,f'{v:.3f}',ha='center',va='center',fontsize=11,
|
||||||
|
fontweight='bold',
|
||||||
|
color='white' if v>attn_m_mc.max()*0.5 else '#0D1B3E')
|
||||||
|
plt.colorbar(im,ax=ax,fraction=0.046)
|
||||||
|
ax.set_title('(i) 어텐션 가중치 α_{u,i} — MAML @ 10dB',fontsize=12,fontweight='bold')
|
||||||
|
ax.set_xlabel('참조 사용자 i',fontsize=10); ax.set_ylabel('질의 사용자 u',fontsize=10)
|
||||||
|
|
||||||
|
fig.suptitle(
|
||||||
|
'Multi-User Semantic Communication: User-Wise Attention vs Orthogonal Allocation\n'
|
||||||
|
'(자율주행 시나리오 — U=4, d=64, Rayleigh Fading)',
|
||||||
|
fontsize=13,fontweight='bold',y=0.97)
|
||||||
|
|
||||||
|
plt.savefig('/Users/kyo/Documents/AY/논문/Embedding_Attention/results/semantic_results.png',dpi=150,
|
||||||
|
bbox_inches='tight',facecolor='#F8F9FA')
|
||||||
|
plt.close()
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════
|
||||||
|
# 7. SUMMARY
|
||||||
|
# ══════════════════════════════════════════════════════════════════════
|
||||||
|
mask=~np.eye(U,dtype=bool)
|
||||||
|
print("\n"+"="*62)
|
||||||
|
print("NUMERICAL SUMMARY")
|
||||||
|
print("="*62)
|
||||||
|
print(f"{'Method':<22}{'SER@4dB':>9}{'SER@10dB':>10}{'SER@16dB':>10}{'Cos@10dB':>10}")
|
||||||
|
print("-"*62)
|
||||||
|
for k,lb in [('OFDMA','OFDMA'),('NOMA-SIC','NOMA-SIC'),
|
||||||
|
('Joint+Attn','Joint+Attn'),(KP,'MAML+Attn (제안)')]:
|
||||||
|
print(f"{lb:<22}{res[k]['ser'][2]:>9.4f}{res[k]['ser'][5]:>10.4f}"
|
||||||
|
f"{res[k]['ser'][8]:>10.4f}{res[k]['cos'][5]:>10.4f}")
|
||||||
|
print(f"\n임베딩 상관계수 |ρ| (off-diag @ 10 dB):")
|
||||||
|
print(f" Joint : mean={np.abs(rho_j[mask]).mean():.4f} "
|
||||||
|
f"[{rho_j[mask].min():.4f}, {rho_j[mask].max():.4f}]")
|
||||||
|
print(f" MAML : mean={np.abs(rho_m[mask]).mean():.4f} "
|
||||||
|
f"[{rho_m[mask].min():.4f}, {rho_m[mask].max():.4f}]")
|
||||||
|
print(f"\n어텐션 가중치 α (MAML @ 10 dB):")
|
||||||
|
hdr=''.join([f" U{j+1}" for j in range(U)])
|
||||||
|
print(f"{'':>14}{hdr}")
|
||||||
|
for i in range(U):
|
||||||
|
row=''.join([f" {attn_m_mc[i,j]:>7.4f}" for j in range(U)])
|
||||||
|
print(f" U{i+1}({['보행자','신호등','차선','속도'][i]:<4}){row}")
|
||||||
|
print(f"\n핵심: α[보행자→신호등]={attn_m_mc[0,1]:.4f} (높음) vs "
|
||||||
|
f"α[보행자→속도]={attn_m_mc[0,3]:.4f} (낮음)")
|
||||||
|
print(f" SER 개선 vs OFDMA @ 10dB: {res['OFDMA']['ser'][5]-res[KP]['ser'][5]:.4f}")
|
||||||
|
print("="*62)
|
||||||
|
print("완료! → /home/claude/semantic_results.png")
|
||||||
Executable
+611
@@ -0,0 +1,611 @@
|
|||||||
|
{
|
||||||
|
"snr": [
|
||||||
|
0.0,
|
||||||
|
2.0,
|
||||||
|
4.0,
|
||||||
|
6.0,
|
||||||
|
8.0,
|
||||||
|
10.0,
|
||||||
|
12.0,
|
||||||
|
14.0,
|
||||||
|
16.0,
|
||||||
|
18.0,
|
||||||
|
20.0
|
||||||
|
],
|
||||||
|
"scenarios": {
|
||||||
|
"HIGH": {
|
||||||
|
"uwca": {
|
||||||
|
"ser": [
|
||||||
|
0.79322265625,
|
||||||
|
0.6887890625,
|
||||||
|
0.56841796875,
|
||||||
|
0.45421875,
|
||||||
|
0.3492578125,
|
||||||
|
0.27025390625,
|
||||||
|
0.207265625,
|
||||||
|
0.1657421875,
|
||||||
|
0.13703125,
|
||||||
|
0.12095703125,
|
||||||
|
0.1100390625
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.33795346930623055,
|
||||||
|
0.37925521612167357,
|
||||||
|
0.41644449681043627,
|
||||||
|
0.4479854838550091,
|
||||||
|
0.47490308076143267,
|
||||||
|
0.4951946732401848,
|
||||||
|
0.5113272194564342,
|
||||||
|
0.5240792188048363,
|
||||||
|
0.5319563418626785,
|
||||||
|
0.5371107071638107,
|
||||||
|
0.5412148103117943
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ofdma": {
|
||||||
|
"ser": [
|
||||||
|
0.86265625,
|
||||||
|
0.79513671875,
|
||||||
|
0.72,
|
||||||
|
0.6441796875,
|
||||||
|
0.5701171875,
|
||||||
|
0.508203125,
|
||||||
|
0.4517578125,
|
||||||
|
0.40755859375,
|
||||||
|
0.37095703125,
|
||||||
|
0.34736328125,
|
||||||
|
0.32875
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.29943249210715295,
|
||||||
|
0.33389897614717484,
|
||||||
|
0.36575670570135116,
|
||||||
|
0.39495515793561936,
|
||||||
|
0.4179712519049644,
|
||||||
|
0.4361314806342125,
|
||||||
|
0.45201442047953605,
|
||||||
|
0.46333996921777726,
|
||||||
|
0.4724611184000969,
|
||||||
|
0.4786807146668434,
|
||||||
|
0.48343033745884895
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sfdma": {
|
||||||
|
"ser": [
|
||||||
|
0.86451171875,
|
||||||
|
0.7958984375,
|
||||||
|
0.72001953125,
|
||||||
|
0.6443359375,
|
||||||
|
0.57337890625,
|
||||||
|
0.5067578125,
|
||||||
|
0.45134765625,
|
||||||
|
0.40892578125,
|
||||||
|
0.3776171875,
|
||||||
|
0.3498828125,
|
||||||
|
0.3262109375
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.2977725945413113,
|
||||||
|
0.33308099195361135,
|
||||||
|
0.36644414871931075,
|
||||||
|
0.39391879171133043,
|
||||||
|
0.4170221574604511,
|
||||||
|
0.43710611209273337,
|
||||||
|
0.4524139855802059,
|
||||||
|
0.46328682363033297,
|
||||||
|
0.4710295398533344,
|
||||||
|
0.47808450981974604,
|
||||||
|
0.4834167508780956
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"noma": {
|
||||||
|
"ser": [
|
||||||
|
0.397890625,
|
||||||
|
0.2672265625,
|
||||||
|
0.19328125,
|
||||||
|
0.171796875,
|
||||||
|
0.1758203125,
|
||||||
|
0.18076171875,
|
||||||
|
0.1905859375,
|
||||||
|
0.20037109375,
|
||||||
|
0.2037109375,
|
||||||
|
0.20857421875,
|
||||||
|
0.21048828125
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.47271593764424324,
|
||||||
|
0.5070651569962501,
|
||||||
|
0.5199588078260422,
|
||||||
|
0.5122481778264045,
|
||||||
|
0.49796718955039976,
|
||||||
|
0.488938904106617,
|
||||||
|
0.4789886870980263,
|
||||||
|
0.4708588546514511,
|
||||||
|
0.46874344900250436,
|
||||||
|
0.4639585228264332,
|
||||||
|
0.462770057618618
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lmmse_blind": {
|
||||||
|
"ser": [
|
||||||
|
0.8617578125,
|
||||||
|
0.799921875,
|
||||||
|
0.72150390625,
|
||||||
|
0.643828125,
|
||||||
|
0.56947265625,
|
||||||
|
0.50923828125,
|
||||||
|
0.45154296875,
|
||||||
|
0.4073046875,
|
||||||
|
0.37138671875,
|
||||||
|
0.35033203125,
|
||||||
|
0.3273828125
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.2984296010434628,
|
||||||
|
0.3334098918735981,
|
||||||
|
0.36603861942887306,
|
||||||
|
0.39393075689673424,
|
||||||
|
0.41776161566376685,
|
||||||
|
0.4366233333945274,
|
||||||
|
0.45187092885375024,
|
||||||
|
0.4637425647675991,
|
||||||
|
0.4722751374542713,
|
||||||
|
0.47823858007788655,
|
||||||
|
0.48371231377124785
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lmmse_genie": {
|
||||||
|
"ser": [
|
||||||
|
0.7064453125,
|
||||||
|
0.57869140625,
|
||||||
|
0.44806640625,
|
||||||
|
0.3400390625,
|
||||||
|
0.23875,
|
||||||
|
0.17251953125,
|
||||||
|
0.124375,
|
||||||
|
0.0902734375,
|
||||||
|
0.06853515625,
|
||||||
|
0.05453125,
|
||||||
|
0.04357421875
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.3729763299226761,
|
||||||
|
0.41429273143410683,
|
||||||
|
0.4526737867295742,
|
||||||
|
0.4834487026929855,
|
||||||
|
0.512170577198267,
|
||||||
|
0.5344957360625266,
|
||||||
|
0.5517739492654801,
|
||||||
|
0.5652388066053391,
|
||||||
|
0.5750497043132782,
|
||||||
|
0.5823147013783455,
|
||||||
|
0.58900510430336
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tdma_proj": {
|
||||||
|
"ser": [
|
||||||
|
0.8651171875,
|
||||||
|
0.80091796875,
|
||||||
|
0.726015625,
|
||||||
|
0.64373046875,
|
||||||
|
0.56970703125,
|
||||||
|
0.506484375,
|
||||||
|
0.45396484375,
|
||||||
|
0.408828125,
|
||||||
|
0.36921875,
|
||||||
|
0.3469140625,
|
||||||
|
0.32806640625
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.29885986328125,
|
||||||
|
0.3340367446839809,
|
||||||
|
0.36605709329247477,
|
||||||
|
0.39507389038801194,
|
||||||
|
0.41877222195267677,
|
||||||
|
0.4366185148060322,
|
||||||
|
0.45217046469449995,
|
||||||
|
0.46392689675092696,
|
||||||
|
0.4729145887494087,
|
||||||
|
0.4781847970187664,
|
||||||
|
0.4829827207326889
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"LOW": {
|
||||||
|
"uwca": {
|
||||||
|
"ser": [
|
||||||
|
0.86876953125,
|
||||||
|
0.80390625,
|
||||||
|
0.7337890625,
|
||||||
|
0.65740234375,
|
||||||
|
0.5919921875,
|
||||||
|
0.52013671875,
|
||||||
|
0.4721484375,
|
||||||
|
0.42322265625,
|
||||||
|
0.40064453125,
|
||||||
|
0.36935546875,
|
||||||
|
0.34853515625
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.29597470179200175,
|
||||||
|
0.33109628096222876,
|
||||||
|
0.36099798038601877,
|
||||||
|
0.3899730713665485,
|
||||||
|
0.4126627291738987,
|
||||||
|
0.43307775735855103,
|
||||||
|
0.4478067138791084,
|
||||||
|
0.4601286995410919,
|
||||||
|
0.4664891073107719,
|
||||||
|
0.47376771569252013,
|
||||||
|
0.47888876497745514
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ofdma": {
|
||||||
|
"ser": [
|
||||||
|
0.863359375,
|
||||||
|
0.79568359375,
|
||||||
|
0.7214453125,
|
||||||
|
0.642421875,
|
||||||
|
0.575625,
|
||||||
|
0.5086328125,
|
||||||
|
0.446640625,
|
||||||
|
0.40607421875,
|
||||||
|
0.37544921875,
|
||||||
|
0.3430859375,
|
||||||
|
0.32484375
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.29824803382158277,
|
||||||
|
0.3340535229444504,
|
||||||
|
0.3654038654267788,
|
||||||
|
0.3942600616812706,
|
||||||
|
0.41689895421266554,
|
||||||
|
0.4364903150498867,
|
||||||
|
0.45267546772956846,
|
||||||
|
0.4638157272338867,
|
||||||
|
0.47199389144778253,
|
||||||
|
0.4796067886054516,
|
||||||
|
0.484298942387104
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sfdma": {
|
||||||
|
"ser": [
|
||||||
|
0.861328125,
|
||||||
|
0.796875,
|
||||||
|
0.71943359375,
|
||||||
|
0.6455078125,
|
||||||
|
0.569296875,
|
||||||
|
0.50705078125,
|
||||||
|
0.44462890625,
|
||||||
|
0.41060546875,
|
||||||
|
0.37119140625,
|
||||||
|
0.34802734375,
|
||||||
|
0.324765625
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.30050423219799993,
|
||||||
|
0.33365500897169115,
|
||||||
|
0.3652912409603596,
|
||||||
|
0.3940856830775738,
|
||||||
|
0.4177810078859329,
|
||||||
|
0.4366115701198578,
|
||||||
|
0.45307490915060045,
|
||||||
|
0.46283702448010444,
|
||||||
|
0.4725301179289818,
|
||||||
|
0.47858723148703575,
|
||||||
|
0.4841072130203247
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"noma": {
|
||||||
|
"ser": [
|
||||||
|
0.7743359375,
|
||||||
|
0.7134765625,
|
||||||
|
0.6685546875,
|
||||||
|
0.6380859375,
|
||||||
|
0.618828125,
|
||||||
|
0.6063671875,
|
||||||
|
0.6016015625,
|
||||||
|
0.59298828125,
|
||||||
|
0.59419921875,
|
||||||
|
0.59216796875,
|
||||||
|
0.59181640625
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.2506940153986216,
|
||||||
|
0.2522821005433798,
|
||||||
|
0.253538987711072,
|
||||||
|
0.2523435969650745,
|
||||||
|
0.2522695341706276,
|
||||||
|
0.25104541644454004,
|
||||||
|
0.2523400906473398,
|
||||||
|
0.25208909578621386,
|
||||||
|
0.2518411290645599,
|
||||||
|
0.25129005320370196,
|
||||||
|
0.25262403398752215
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lmmse_blind": {
|
||||||
|
"ser": [
|
||||||
|
0.8632421875,
|
||||||
|
0.7934765625,
|
||||||
|
0.72123046875,
|
||||||
|
0.6446875,
|
||||||
|
0.5729296875,
|
||||||
|
0.50517578125,
|
||||||
|
0.45265625,
|
||||||
|
0.4099609375,
|
||||||
|
0.37396484375,
|
||||||
|
0.3478515625,
|
||||||
|
0.33015625
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.2980802461504936,
|
||||||
|
0.335287110209465,
|
||||||
|
0.3664214126765728,
|
||||||
|
0.39368403509259225,
|
||||||
|
0.41743764773011205,
|
||||||
|
0.43701319947838785,
|
||||||
|
0.4511328709125519,
|
||||||
|
0.46285511627793313,
|
||||||
|
0.4718122765421867,
|
||||||
|
0.4787981267273426,
|
||||||
|
0.4830517227947712
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lmmse_genie": {
|
||||||
|
"ser": [
|
||||||
|
0.86291015625,
|
||||||
|
0.7972265625,
|
||||||
|
0.720625,
|
||||||
|
0.64396484375,
|
||||||
|
0.576796875,
|
||||||
|
0.50388671875,
|
||||||
|
0.45048828125,
|
||||||
|
0.410859375,
|
||||||
|
0.37306640625,
|
||||||
|
0.3459375,
|
||||||
|
0.32337890625
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.2987270838022232,
|
||||||
|
0.3340585994720459,
|
||||||
|
0.36559688225388526,
|
||||||
|
0.39470515951514246,
|
||||||
|
0.4167175969481468,
|
||||||
|
0.437459883838892,
|
||||||
|
0.45222372278571127,
|
||||||
|
0.4626065303385258,
|
||||||
|
0.472180430740118,
|
||||||
|
0.478725159317255,
|
||||||
|
0.48388352036476134
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tdma_proj": {
|
||||||
|
"ser": [
|
||||||
|
0.8632421875,
|
||||||
|
0.7973046875,
|
||||||
|
0.71634765625,
|
||||||
|
0.64626953125,
|
||||||
|
0.57171875,
|
||||||
|
0.50541015625,
|
||||||
|
0.45244140625,
|
||||||
|
0.41146484375,
|
||||||
|
0.3691015625,
|
||||||
|
0.3478125,
|
||||||
|
0.3271875
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.29940737307071685,
|
||||||
|
0.3341533626616001,
|
||||||
|
0.36738314867019656,
|
||||||
|
0.3947581851482391,
|
||||||
|
0.41644699349999426,
|
||||||
|
0.437353357821703,
|
||||||
|
0.45220112934708595,
|
||||||
|
0.46335226818919184,
|
||||||
|
0.4732473592460156,
|
||||||
|
0.4784095577895641,
|
||||||
|
0.482913865596056
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"MIX": {
|
||||||
|
"uwca": {
|
||||||
|
"ser": [
|
||||||
|
0.84638671875,
|
||||||
|
0.77642578125,
|
||||||
|
0.69482421875,
|
||||||
|
0.6133203125,
|
||||||
|
0.5303515625,
|
||||||
|
0.46197265625,
|
||||||
|
0.40923828125,
|
||||||
|
0.36640625,
|
||||||
|
0.32798828125,
|
||||||
|
0.30064453125,
|
||||||
|
0.2868359375
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.3073283612728119,
|
||||||
|
0.34259016752243043,
|
||||||
|
0.3753962276875973,
|
||||||
|
0.4042992687225342,
|
||||||
|
0.42905311986804007,
|
||||||
|
0.4474186111986637,
|
||||||
|
0.4616049011051655,
|
||||||
|
0.4726345820724964,
|
||||||
|
0.48257728427648544,
|
||||||
|
0.48964122965931894,
|
||||||
|
0.49293914198875427
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ofdma": {
|
||||||
|
"ser": [
|
||||||
|
0.86296875,
|
||||||
|
0.7978125,
|
||||||
|
0.7214453125,
|
||||||
|
0.64078125,
|
||||||
|
0.5704296875,
|
||||||
|
0.50927734375,
|
||||||
|
0.45046875,
|
||||||
|
0.4148046875,
|
||||||
|
0.3720703125,
|
||||||
|
0.34546875,
|
||||||
|
0.32658203125
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.2996881593763828,
|
||||||
|
0.33301528945565223,
|
||||||
|
0.36559657275676727,
|
||||||
|
0.39512176558375356,
|
||||||
|
0.41771291017532347,
|
||||||
|
0.4362964195013046,
|
||||||
|
0.45225114360451696,
|
||||||
|
0.46220010906457903,
|
||||||
|
0.47270380780100824,
|
||||||
|
0.4790311701595783,
|
||||||
|
0.4835458607971668
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sfdma": {
|
||||||
|
"ser": [
|
||||||
|
0.8665625,
|
||||||
|
0.79666015625,
|
||||||
|
0.72443359375,
|
||||||
|
0.64232421875,
|
||||||
|
0.5667578125,
|
||||||
|
0.50875,
|
||||||
|
0.45208984375,
|
||||||
|
0.40546875,
|
||||||
|
0.3686328125,
|
||||||
|
0.34744140625,
|
||||||
|
0.32927734375
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.2980777567625046,
|
||||||
|
0.33418346554040906,
|
||||||
|
0.3653854741156101,
|
||||||
|
0.3952876031398773,
|
||||||
|
0.4191733206808567,
|
||||||
|
0.43588161647319795,
|
||||||
|
0.4515273554623127,
|
||||||
|
0.4639491982758045,
|
||||||
|
0.4729758943617344,
|
||||||
|
0.4787950477004051,
|
||||||
|
0.48281720593571664
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"noma": {
|
||||||
|
"ser": [
|
||||||
|
0.67751953125,
|
||||||
|
0.60744140625,
|
||||||
|
0.54779296875,
|
||||||
|
0.51068359375,
|
||||||
|
0.4876953125,
|
||||||
|
0.47490234375,
|
||||||
|
0.46716796875,
|
||||||
|
0.46134765625,
|
||||||
|
0.45658203125,
|
||||||
|
0.45490234375,
|
||||||
|
0.45447265625
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.31611435264348986,
|
||||||
|
0.32969766601920125,
|
||||||
|
0.33653339073061944,
|
||||||
|
0.34149627268314364,
|
||||||
|
0.344899190813303,
|
||||||
|
0.3487969210743904,
|
||||||
|
0.34979948461055754,
|
||||||
|
0.3526943688094616,
|
||||||
|
0.354361120313406,
|
||||||
|
0.3555732563138008,
|
||||||
|
0.35504164710640906
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lmmse_blind": {
|
||||||
|
"ser": [
|
||||||
|
0.86271484375,
|
||||||
|
0.79595703125,
|
||||||
|
0.71880859375,
|
||||||
|
0.6420703125,
|
||||||
|
0.57037109375,
|
||||||
|
0.50509765625,
|
||||||
|
0.45580078125,
|
||||||
|
0.4087109375,
|
||||||
|
0.37091796875,
|
||||||
|
0.352421875,
|
||||||
|
0.326640625
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.2985852397978306,
|
||||||
|
0.3338994240760803,
|
||||||
|
0.36584676772356034,
|
||||||
|
0.39458225801587105,
|
||||||
|
0.41804412424564363,
|
||||||
|
0.43662389501929283,
|
||||||
|
0.45104776993393897,
|
||||||
|
0.4632842779159546,
|
||||||
|
0.47248409807682035,
|
||||||
|
0.4778492307662964,
|
||||||
|
0.4835035181045532
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lmmse_genie": {
|
||||||
|
"ser": [
|
||||||
|
0.83638671875,
|
||||||
|
0.75751953125,
|
||||||
|
0.669375,
|
||||||
|
0.58080078125,
|
||||||
|
0.50162109375,
|
||||||
|
0.43412109375,
|
||||||
|
0.36638671875,
|
||||||
|
0.32205078125,
|
||||||
|
0.291953125,
|
||||||
|
0.26453125,
|
||||||
|
0.244453125
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.3141723415255547,
|
||||||
|
0.35132803007960317,
|
||||||
|
0.38337485924363135,
|
||||||
|
0.41298850655555724,
|
||||||
|
0.4358947241306305,
|
||||||
|
0.45558819562196734,
|
||||||
|
0.47349897637963295,
|
||||||
|
0.48520163267850874,
|
||||||
|
0.4926259195804596,
|
||||||
|
0.5009401620924473,
|
||||||
|
0.506167941391468
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tdma_proj": {
|
||||||
|
"ser": [
|
||||||
|
0.86150390625,
|
||||||
|
0.7968359375,
|
||||||
|
0.72208984375,
|
||||||
|
0.63982421875,
|
||||||
|
0.56921875,
|
||||||
|
0.50787109375,
|
||||||
|
0.4481640625,
|
||||||
|
0.40513671875,
|
||||||
|
0.3756640625,
|
||||||
|
0.34693359375,
|
||||||
|
0.32443359375
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.2997307339310646,
|
||||||
|
0.3358344969153404,
|
||||||
|
0.36650204077363013,
|
||||||
|
0.39529991313815116,
|
||||||
|
0.41842835292220115,
|
||||||
|
0.4359996871650219,
|
||||||
|
0.4527186694741249,
|
||||||
|
0.4639644405245781,
|
||||||
|
0.47169492051005363,
|
||||||
|
0.47912489891052246,
|
||||||
|
0.48450767815113066
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+196
@@ -0,0 +1,196 @@
|
|||||||
|
{
|
||||||
|
"sigma_phi_deg": [
|
||||||
|
0,
|
||||||
|
5,
|
||||||
|
10,
|
||||||
|
15,
|
||||||
|
20,
|
||||||
|
30
|
||||||
|
],
|
||||||
|
"snr_eval": [
|
||||||
|
10.0,
|
||||||
|
20.0
|
||||||
|
],
|
||||||
|
"curves": {
|
||||||
|
"scalar_augtrain": {
|
||||||
|
"10.0": {
|
||||||
|
"ser": [
|
||||||
|
0.26923828125,
|
||||||
|
0.2685546875,
|
||||||
|
0.27525390625,
|
||||||
|
0.27935546875,
|
||||||
|
0.2933984375,
|
||||||
|
0.33353515625
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.49434818252921103,
|
||||||
|
0.4952818860113621,
|
||||||
|
0.4941158194839954,
|
||||||
|
0.4934180076420307,
|
||||||
|
0.48840575382113455,
|
||||||
|
0.47757710933685305
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"20.0": {
|
||||||
|
"ser": [
|
||||||
|
0.1084765625,
|
||||||
|
0.1080078125,
|
||||||
|
0.11140625,
|
||||||
|
0.111796875,
|
||||||
|
0.112578125,
|
||||||
|
0.12888671875
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.5416895082592964,
|
||||||
|
0.5417140927910805,
|
||||||
|
0.5407627999782563,
|
||||||
|
0.540904797911644,
|
||||||
|
0.5404352927207947,
|
||||||
|
0.5352037853002548
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"complexI_augtrain": {
|
||||||
|
"10.0": {
|
||||||
|
"ser": [
|
||||||
|
0.27505859375,
|
||||||
|
0.27115234375,
|
||||||
|
0.27126953125,
|
||||||
|
0.28126953125,
|
||||||
|
0.2905859375,
|
||||||
|
0.32849609375
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.49374089896678924,
|
||||||
|
0.4948993918299675,
|
||||||
|
0.49411537259817123,
|
||||||
|
0.49206591010093687,
|
||||||
|
0.4898021328449249,
|
||||||
|
0.4779812078177929
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"20.0": {
|
||||||
|
"ser": [
|
||||||
|
0.1134375,
|
||||||
|
0.113359375,
|
||||||
|
0.11392578125,
|
||||||
|
0.1113671875,
|
||||||
|
0.1164453125,
|
||||||
|
0.1300390625
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.5397938752174377,
|
||||||
|
0.5402382054924965,
|
||||||
|
0.5402522572875023,
|
||||||
|
0.5409401589632035,
|
||||||
|
0.5399208971858025,
|
||||||
|
0.5344709008932114
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"complexIQ_iqtrain": {
|
||||||
|
"10.0": {
|
||||||
|
"ser": [
|
||||||
|
0.2708203125,
|
||||||
|
0.26982421875,
|
||||||
|
0.27427734375,
|
||||||
|
0.28220703125,
|
||||||
|
0.29095703125,
|
||||||
|
0.33181640625
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.4952242363989353,
|
||||||
|
0.49554425284266473,
|
||||||
|
0.494214625954628,
|
||||||
|
0.49156691521406176,
|
||||||
|
0.4892462977766991,
|
||||||
|
0.47812732368707656
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"20.0": {
|
||||||
|
"ser": [
|
||||||
|
0.109921875,
|
||||||
|
0.111875,
|
||||||
|
0.11259765625,
|
||||||
|
0.11376953125,
|
||||||
|
0.1150390625,
|
||||||
|
0.13318359375
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.5412762901186943,
|
||||||
|
0.5405166742205619,
|
||||||
|
0.540867666900158,
|
||||||
|
0.5394987732172012,
|
||||||
|
0.5398257005214692,
|
||||||
|
0.5339345020055771
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"complexI_zerotrain": {
|
||||||
|
"10.0": {
|
||||||
|
"ser": [
|
||||||
|
0.2697265625,
|
||||||
|
0.2716015625,
|
||||||
|
0.27845703125,
|
||||||
|
0.286171875,
|
||||||
|
0.29109375,
|
||||||
|
0.331875
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.4953330734372139,
|
||||||
|
0.4946722063422203,
|
||||||
|
0.4931570044159889,
|
||||||
|
0.4913961146771908,
|
||||||
|
0.4892618091404438,
|
||||||
|
0.4782075408101082
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"20.0": {
|
||||||
|
"ser": [
|
||||||
|
0.1097265625,
|
||||||
|
0.1112109375,
|
||||||
|
0.11009765625,
|
||||||
|
0.11314453125,
|
||||||
|
0.11447265625,
|
||||||
|
0.1271484375
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.5411131280660629,
|
||||||
|
0.540622621178627,
|
||||||
|
0.5416908550262451,
|
||||||
|
0.5399159497022629,
|
||||||
|
0.5392417126893997,
|
||||||
|
0.5353547763824463
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mask_overlap_m_real": {
|
||||||
|
"mean": 0.26302748918533325,
|
||||||
|
"max": 0.3177069127559662
|
||||||
|
},
|
||||||
|
"mask_overlap_m_iq": {
|
||||||
|
"mean": 0.2632656991481781,
|
||||||
|
"max": 0.3174257278442383
|
||||||
|
},
|
||||||
|
"csi_error": {
|
||||||
|
"h_err": [
|
||||||
|
0.0,
|
||||||
|
0.05,
|
||||||
|
0.1,
|
||||||
|
0.2
|
||||||
|
],
|
||||||
|
"uwca_ser": [
|
||||||
|
0.2739453125,
|
||||||
|
0.27005859375,
|
||||||
|
0.27060546875,
|
||||||
|
0.27416015625
|
||||||
|
],
|
||||||
|
"sfdma_ser": [
|
||||||
|
0.508515625,
|
||||||
|
0.510546875,
|
||||||
|
0.5090234375,
|
||||||
|
0.51283203125
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+181
@@ -0,0 +1,181 @@
|
|||||||
|
{
|
||||||
|
"k": [
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
8
|
||||||
|
],
|
||||||
|
"snr_eval": [
|
||||||
|
10.0,
|
||||||
|
20.0
|
||||||
|
],
|
||||||
|
"activity": {
|
||||||
|
"10.0": [
|
||||||
|
[
|
||||||
|
0.9118359375,
|
||||||
|
0.33101536214351657
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.9060677099227905,
|
||||||
|
0.33357466995716095
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.89970703125,
|
||||||
|
0.33690863102674484
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.8868593761324882,
|
||||||
|
0.34056667000055313
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.8798437479138375,
|
||||||
|
0.3450345255434513
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.8694642865657807,
|
||||||
|
0.3481388032436371
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.8603125,
|
||||||
|
0.35211494997143744
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"20.0": [
|
||||||
|
[
|
||||||
|
0.896796875,
|
||||||
|
0.34280089363455774
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.8785937488079071,
|
||||||
|
0.35016911059617994
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.86478515625,
|
||||||
|
0.3569996301829815
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.8461406251788139,
|
||||||
|
0.36452872648835183
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.827643229663372,
|
||||||
|
0.37127331241965295
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.8049330338835716,
|
||||||
|
0.37870371311903
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.780751953125,
|
||||||
|
0.38524287804961205
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"fixed8": {
|
||||||
|
"10.0": [
|
||||||
|
[
|
||||||
|
0.8960546875,
|
||||||
|
0.3391794848442078
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.8792968752980233,
|
||||||
|
0.3480608442425728
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.85390625,
|
||||||
|
0.35765963658690453
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.8286250007152557,
|
||||||
|
0.36636610567569733
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.8018750005960464,
|
||||||
|
0.3757343140244484
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.7726897311210632,
|
||||||
|
0.3843724133074284
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.74373046875,
|
||||||
|
0.3927574257552624
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"20.0": [
|
||||||
|
[
|
||||||
|
0.8806640625,
|
||||||
|
0.34932656660676004
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.8478645813465119,
|
||||||
|
0.3636595942080021
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.806015625,
|
||||||
|
0.377073904722929
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.7695312502980233,
|
||||||
|
0.3878504756093025
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.7219661456346512,
|
||||||
|
0.40071297124028205
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.6728571423888207,
|
||||||
|
0.4125585229694843
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.620732421875,
|
||||||
|
0.4240312688052654
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"oracle": {
|
||||||
|
"2": {
|
||||||
|
"10.0": [
|
||||||
|
0.9151953125,
|
||||||
|
0.32624885395169256
|
||||||
|
],
|
||||||
|
"20.0": [
|
||||||
|
0.905078125,
|
||||||
|
0.33809939414262774
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
"10.0": [
|
||||||
|
0.92490234375,
|
||||||
|
0.31790074944496155
|
||||||
|
],
|
||||||
|
"20.0": [
|
||||||
|
0.90427734375,
|
||||||
|
0.33895289853215216
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"6": {
|
||||||
|
"10.0": [
|
||||||
|
0.8479557287693024,
|
||||||
|
0.361840800344944
|
||||||
|
],
|
||||||
|
"20.0": [
|
||||||
|
0.7785937511920928,
|
||||||
|
0.3879467982053757
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"8": {
|
||||||
|
"10.0": [
|
||||||
|
0.73982421875,
|
||||||
|
0.39335060492157936
|
||||||
|
],
|
||||||
|
"20.0": [
|
||||||
|
0.617890625,
|
||||||
|
0.42435349345207213
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+151
@@ -0,0 +1,151 @@
|
|||||||
|
{
|
||||||
|
"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
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+245
@@ -0,0 +1,245 @@
|
|||||||
|
{
|
||||||
|
"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
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+245
@@ -0,0 +1,245 @@
|
|||||||
|
{
|
||||||
|
"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.30435546875,
|
||||||
|
0.4863732597231865
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.32873046875,
|
||||||
|
0.47999764248728755
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.37107421875,
|
||||||
|
0.46947461530566215
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.45490234375,
|
||||||
|
0.44783866554498675
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"20.0": [
|
||||||
|
[
|
||||||
|
0.11287109375,
|
||||||
|
0.5400555384159088
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.14697265625,
|
||||||
|
0.5300638779997826
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.1816015625,
|
||||||
|
0.5197082979977131
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.2347265625,
|
||||||
|
0.5055944377183914
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.33798828125,
|
||||||
|
0.47965065121650696
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"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.445234375,
|
||||||
|
0.412728616297245
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.4810546875,
|
||||||
|
0.40005304843187334
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.53046875,
|
||||||
|
0.38354199185967447
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.5988671875,
|
||||||
|
0.3615588688850403
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"20.0": [
|
||||||
|
[
|
||||||
|
0.11326171875,
|
||||||
|
0.5398727428913116
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.31857421875,
|
||||||
|
0.4483929005265236
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.3642578125,
|
||||||
|
0.43187748357653616
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.4175390625,
|
||||||
|
0.41587104722857476
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.50896484375,
|
||||||
|
0.3869159772992134
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+247
@@ -0,0 +1,247 @@
|
|||||||
|
{
|
||||||
|
"snr": [
|
||||||
|
0.0,
|
||||||
|
2.0,
|
||||||
|
4.0,
|
||||||
|
6.0,
|
||||||
|
8.0,
|
||||||
|
10.0,
|
||||||
|
12.0,
|
||||||
|
14.0,
|
||||||
|
16.0,
|
||||||
|
18.0,
|
||||||
|
20.0
|
||||||
|
],
|
||||||
|
"cases": {
|
||||||
|
"NONLIN-HIGH": {
|
||||||
|
"beta_emp": 0.0061437999829649925,
|
||||||
|
"uwca": {
|
||||||
|
"ser": [
|
||||||
|
0.790390625,
|
||||||
|
0.6993359375,
|
||||||
|
0.60568359375,
|
||||||
|
0.5097265625,
|
||||||
|
0.42337890625,
|
||||||
|
0.36263671875,
|
||||||
|
0.3098046875,
|
||||||
|
0.26396484375,
|
||||||
|
0.243046875,
|
||||||
|
0.23076171875,
|
||||||
|
0.22005859375
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.3278296999633312,
|
||||||
|
0.36553969904780387,
|
||||||
|
0.39952867075800896,
|
||||||
|
0.42956090450286866,
|
||||||
|
0.4540892793238163,
|
||||||
|
0.47176132932305337,
|
||||||
|
0.486503779143095,
|
||||||
|
0.49822831645607946,
|
||||||
|
0.504666059166193,
|
||||||
|
0.5088490030169487,
|
||||||
|
0.5123565834760666
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ofdma": {
|
||||||
|
"ser": [
|
||||||
|
0.86720703125,
|
||||||
|
0.80474609375,
|
||||||
|
0.72923828125,
|
||||||
|
0.65044921875,
|
||||||
|
0.5783203125,
|
||||||
|
0.51802734375,
|
||||||
|
0.45974609375,
|
||||||
|
0.41630859375,
|
||||||
|
0.37927734375,
|
||||||
|
0.35287109375,
|
||||||
|
0.3345703125
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.29808355286717414,
|
||||||
|
0.332402256578207,
|
||||||
|
0.3646286201477051,
|
||||||
|
0.3923575595021248,
|
||||||
|
0.4157045525312424,
|
||||||
|
0.43456896290183067,
|
||||||
|
0.45029769226908684,
|
||||||
|
0.46131571754813194,
|
||||||
|
0.4703022360801697,
|
||||||
|
0.4764167308807373,
|
||||||
|
0.4811262246966362
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"noma": {
|
||||||
|
"ser": [
|
||||||
|
0.77013671875,
|
||||||
|
0.712109375,
|
||||||
|
0.66578125,
|
||||||
|
0.634609375,
|
||||||
|
0.61708984375,
|
||||||
|
0.60466796875,
|
||||||
|
0.59591796875,
|
||||||
|
0.593671875,
|
||||||
|
0.59068359375,
|
||||||
|
0.5886328125,
|
||||||
|
0.58640625
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.2531153728067875,
|
||||||
|
0.2549177658557892,
|
||||||
|
0.2538824989646673,
|
||||||
|
0.2536986283957958,
|
||||||
|
0.2540162181854248,
|
||||||
|
0.2535675698518753,
|
||||||
|
0.25407157935202124,
|
||||||
|
0.25388947516679766,
|
||||||
|
0.25431982301175593,
|
||||||
|
0.2536552938073873,
|
||||||
|
0.25504677280783655
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lmmse_genie": {
|
||||||
|
"ser": [
|
||||||
|
0.86916015625,
|
||||||
|
0.80337890625,
|
||||||
|
0.73052734375,
|
||||||
|
0.65587890625,
|
||||||
|
0.5792578125,
|
||||||
|
0.5153515625,
|
||||||
|
0.46029296875,
|
||||||
|
0.4175390625,
|
||||||
|
0.37681640625,
|
||||||
|
0.35603515625,
|
||||||
|
0.33283203125
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.2973393540084362,
|
||||||
|
0.33171318054199217,
|
||||||
|
0.3636562559008598,
|
||||||
|
0.39189154386520386,
|
||||||
|
0.4159616154432297,
|
||||||
|
0.43497667729854583,
|
||||||
|
0.449855944365263,
|
||||||
|
0.461167299002409,
|
||||||
|
0.47068964451551437,
|
||||||
|
0.4762733347713947,
|
||||||
|
0.48112414956092836
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"NONLIN-LOW": {
|
||||||
|
"beta_emp": 0.001806730404496193,
|
||||||
|
"uwca": {
|
||||||
|
"ser": [
|
||||||
|
0.8244921875,
|
||||||
|
0.7410546875,
|
||||||
|
0.66017578125,
|
||||||
|
0.57970703125,
|
||||||
|
0.50078125,
|
||||||
|
0.44056640625,
|
||||||
|
0.3893359375,
|
||||||
|
0.35234375,
|
||||||
|
0.324296875,
|
||||||
|
0.3056640625,
|
||||||
|
0.29314453125
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.3093454629182816,
|
||||||
|
0.3472594168782234,
|
||||||
|
0.37886988461017607,
|
||||||
|
0.40726904839277267,
|
||||||
|
0.43167252153158187,
|
||||||
|
0.4491182991862297,
|
||||||
|
0.4645391863584518,
|
||||||
|
0.474527428150177,
|
||||||
|
0.4819556805491447,
|
||||||
|
0.487779633551836,
|
||||||
|
0.4909189411997795
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ofdma": {
|
||||||
|
"ser": [
|
||||||
|
0.869609375,
|
||||||
|
0.80314453125,
|
||||||
|
0.72744140625,
|
||||||
|
0.651953125,
|
||||||
|
0.57908203125,
|
||||||
|
0.514296875,
|
||||||
|
0.458984375,
|
||||||
|
0.41361328125,
|
||||||
|
0.38015625,
|
||||||
|
0.35447265625,
|
||||||
|
0.33357421875
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.2971385581791401,
|
||||||
|
0.3324370531737804,
|
||||||
|
0.3640595290064812,
|
||||||
|
0.3918473927676678,
|
||||||
|
0.4165782316029072,
|
||||||
|
0.43477963089942934,
|
||||||
|
0.4495477384328842,
|
||||||
|
0.4618768581748009,
|
||||||
|
0.47005663082003596,
|
||||||
|
0.4766060091555119,
|
||||||
|
0.48131546869874
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"noma": {
|
||||||
|
"ser": [
|
||||||
|
0.77365234375,
|
||||||
|
0.71427734375,
|
||||||
|
0.66837890625,
|
||||||
|
0.64001953125,
|
||||||
|
0.6203515625,
|
||||||
|
0.6087109375,
|
||||||
|
0.60255859375,
|
||||||
|
0.597265625,
|
||||||
|
0.59212890625,
|
||||||
|
0.5930078125,
|
||||||
|
0.58919921875
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.25110539749264715,
|
||||||
|
0.2537043013423681,
|
||||||
|
0.25317927345633506,
|
||||||
|
0.25191925302147866,
|
||||||
|
0.25150172002613547,
|
||||||
|
0.2508468548953533,
|
||||||
|
0.2522956795990467,
|
||||||
|
0.25112451463937757,
|
||||||
|
0.2523871362954378,
|
||||||
|
0.2514989008009434,
|
||||||
|
0.25212071448564527
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lmmse_genie": {
|
||||||
|
"ser": [
|
||||||
|
0.870390625,
|
||||||
|
0.80353515625,
|
||||||
|
0.731953125,
|
||||||
|
0.65462890625,
|
||||||
|
0.58052734375,
|
||||||
|
0.5126171875,
|
||||||
|
0.4633203125,
|
||||||
|
0.41298828125,
|
||||||
|
0.37841796875,
|
||||||
|
0.355078125,
|
||||||
|
0.3344921875
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.29619003131985666,
|
||||||
|
0.3314328114688396,
|
||||||
|
0.3639054714143276,
|
||||||
|
0.39149928227066993,
|
||||||
|
0.4152733239531517,
|
||||||
|
0.4352148090302944,
|
||||||
|
0.4494645407795906,
|
||||||
|
0.46180440217256546,
|
||||||
|
0.47014191970229147,
|
||||||
|
0.47618671402335166,
|
||||||
|
0.4810084080696106
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+98
@@ -0,0 +1,98 @@
|
|||||||
|
{
|
||||||
|
"snr": [
|
||||||
|
0.0,
|
||||||
|
2.0,
|
||||||
|
4.0,
|
||||||
|
6.0,
|
||||||
|
8.0,
|
||||||
|
10.0,
|
||||||
|
12.0,
|
||||||
|
14.0,
|
||||||
|
16.0,
|
||||||
|
18.0,
|
||||||
|
20.0
|
||||||
|
],
|
||||||
|
"beta_uv_mean": 0.3904166666666667,
|
||||||
|
"uwca": {
|
||||||
|
"rho_input": [
|
||||||
|
0.38956392144163443,
|
||||||
|
0.38775963683923087,
|
||||||
|
0.3880219903588295,
|
||||||
|
0.3881618897616864,
|
||||||
|
0.38777250061432533,
|
||||||
|
0.3863464270035427,
|
||||||
|
0.38830024401346847,
|
||||||
|
0.38755521948138877,
|
||||||
|
0.38766041909654947,
|
||||||
|
0.38740170896053316,
|
||||||
|
0.38578140864769644
|
||||||
|
],
|
||||||
|
"rho_decoded": [
|
||||||
|
0.305231709529956,
|
||||||
|
0.31184700431923085,
|
||||||
|
0.321264610439539,
|
||||||
|
0.3309843138853709,
|
||||||
|
0.34082442497213683,
|
||||||
|
0.35038761819402375,
|
||||||
|
0.3575822949409485,
|
||||||
|
0.36593782161672905,
|
||||||
|
0.37170385092496877,
|
||||||
|
0.3746264092127481,
|
||||||
|
0.3768504020571708
|
||||||
|
],
|
||||||
|
"rho_residual": [
|
||||||
|
0.2304025647044181,
|
||||||
|
0.21344653452436116,
|
||||||
|
0.19712237288554504,
|
||||||
|
0.18036628514528272,
|
||||||
|
0.16850986543421942,
|
||||||
|
0.15848431913182137,
|
||||||
|
0.15133254030098522,
|
||||||
|
0.14582291372120382,
|
||||||
|
0.14286635869958753,
|
||||||
|
0.13878688212173684,
|
||||||
|
0.13878255244344476
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ofdma": {
|
||||||
|
"rho_input": [
|
||||||
|
0.38715864519278204,
|
||||||
|
0.3887561237315337,
|
||||||
|
0.387941963672638,
|
||||||
|
0.3876616556942464,
|
||||||
|
0.38791437988479943,
|
||||||
|
0.387366031507651,
|
||||||
|
0.3880658547083538,
|
||||||
|
0.3872282171746095,
|
||||||
|
0.3860448093712329,
|
||||||
|
0.3875497603913147,
|
||||||
|
0.3860259728630384
|
||||||
|
],
|
||||||
|
"rho_decoded": [
|
||||||
|
7.473706044644737e-05,
|
||||||
|
7.072256555450921e-05,
|
||||||
|
4.665079778836425e-05,
|
||||||
|
0.00024113242104552518,
|
||||||
|
-0.0001454370894013361,
|
||||||
|
0.00019012298099066048,
|
||||||
|
-0.0001384556134629141,
|
||||||
|
-0.00015057531054480934,
|
||||||
|
-0.00047786584111842486,
|
||||||
|
0.00019809762234217497,
|
||||||
|
-0.00023452785023740343
|
||||||
|
],
|
||||||
|
"rho_residual": [
|
||||||
|
0.1073327248388281,
|
||||||
|
0.09175947538033745,
|
||||||
|
0.07762182657994951,
|
||||||
|
0.06378789233004983,
|
||||||
|
0.051325935235557445,
|
||||||
|
0.04030999146789935,
|
||||||
|
0.031071071475162172,
|
||||||
|
0.024056566797662528,
|
||||||
|
0.017306540097924875,
|
||||||
|
0.013973580723395568,
|
||||||
|
0.010691763695795086
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+1363
File diff suppressed because it is too large
Load Diff
Executable
+368
@@ -0,0 +1,368 @@
|
|||||||
|
{
|
||||||
|
"S_grid": [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
5,
|
||||||
|
10,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"results": {
|
||||||
|
"ricianK20_phi15_snr10": {
|
||||||
|
"meta": [
|
||||||
|
{
|
||||||
|
"ser": 0.11427083333333334,
|
||||||
|
"cos": 0.5355387481053671
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.11015625,
|
||||||
|
"cos": 0.5360154775778453
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.11408854166666667,
|
||||||
|
"cos": 0.5351665059725443
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.10979166666666666,
|
||||||
|
"cos": 0.5365686778227489
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.1125,
|
||||||
|
"cos": 0.5353779737154643
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lookup": [
|
||||||
|
{
|
||||||
|
"ser": 0.15755208333333334,
|
||||||
|
"cos": 0.5232695064942042
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.152734375,
|
||||||
|
"cos": 0.523874221642812
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.15614583333333334,
|
||||||
|
"cos": 0.5235742004712423
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.15640625,
|
||||||
|
"cos": 0.5232807958126068
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.15302083333333333,
|
||||||
|
"cos": 0.5241675400733947
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nakagami3_phi5_snr10": {
|
||||||
|
"meta": [
|
||||||
|
{
|
||||||
|
"ser": 0.139296875,
|
||||||
|
"cos": 0.5291851150989533
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.13609375,
|
||||||
|
"cos": 0.5295972474416097
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.13533854166666667,
|
||||||
|
"cos": 0.5308140345414479
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.13786458333333335,
|
||||||
|
"cos": 0.5300244994958242
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.14177083333333335,
|
||||||
|
"cos": 0.5283970713615418
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lookup": [
|
||||||
|
{
|
||||||
|
"ser": 0.18973958333333332,
|
||||||
|
"cos": 0.5160947958628337
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.17627604166666666,
|
||||||
|
"cos": 0.5183902778228124
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.181484375,
|
||||||
|
"cos": 0.5170280895630519
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.18481770833333333,
|
||||||
|
"cos": 0.5159598344564438
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.18231770833333333,
|
||||||
|
"cos": 0.5171361660957337
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"rayleigh_phi20_snr6": {
|
||||||
|
"meta": [
|
||||||
|
{
|
||||||
|
"ser": 0.4521875,
|
||||||
|
"cos": 0.4479432698090871
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.453828125,
|
||||||
|
"cos": 0.4469218909740448
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.45458333333333334,
|
||||||
|
"cos": 0.4469242548942566
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.4532291666666667,
|
||||||
|
"cos": 0.44761434574921927
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.451640625,
|
||||||
|
"cos": 0.4476551973819733
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lookup": [
|
||||||
|
{
|
||||||
|
"ser": 0.49497395833333335,
|
||||||
|
"cos": 0.4364782508214315
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.497421875,
|
||||||
|
"cos": 0.43555598239103954
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.497109375,
|
||||||
|
"cos": 0.43593698064486186
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.49963541666666667,
|
||||||
|
"cos": 0.43608464042345685
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.5034895833333334,
|
||||||
|
"cos": 0.4354030267397563
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ricianK20_phi15_snr18": {
|
||||||
|
"meta": [
|
||||||
|
{
|
||||||
|
"ser": 0.05213541666666666,
|
||||||
|
"cos": 0.5592047119140625
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.049895833333333334,
|
||||||
|
"cos": 0.5594856301943462
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.050494791666666664,
|
||||||
|
"cos": 0.5598108808199564
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.05002604166666667,
|
||||||
|
"cos": 0.5593102443218231
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.051484375,
|
||||||
|
"cos": 0.5590867590904236
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lookup": [
|
||||||
|
{
|
||||||
|
"ser": 0.09625,
|
||||||
|
"cos": 0.5402481619517009
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.093359375,
|
||||||
|
"cos": 0.540210163195928
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.09635416666666667,
|
||||||
|
"cos": 0.5399158314863841
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.09361979166666666,
|
||||||
|
"cos": 0.5402699712912241
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.09244791666666667,
|
||||||
|
"cos": 0.5401132162412008
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indist_rayleigh_snr10": {
|
||||||
|
"meta": [
|
||||||
|
{
|
||||||
|
"ser": 0.24442708333333332,
|
||||||
|
"cos": 0.5030389416217804
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.239375,
|
||||||
|
"cos": 0.5038288209835688
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.24203125,
|
||||||
|
"cos": 0.5033370635906855
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.23822916666666666,
|
||||||
|
"cos": 0.503516077597936
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.23471354166666666,
|
||||||
|
"cos": 0.5050299618641535
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lookup": [
|
||||||
|
{
|
||||||
|
"ser": 0.29236979166666666,
|
||||||
|
"cos": 0.4903800235191981
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.28880208333333335,
|
||||||
|
"cos": 0.4908251702785492
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.28684895833333335,
|
||||||
|
"cos": 0.49163883725802104
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.2840625,
|
||||||
|
"cos": 0.4920005202293396
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ser": 0.2875,
|
||||||
|
"cos": 0.49090512851874035
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"eta_traj": [
|
||||||
|
1.0009888410568237,
|
||||||
|
0.9988142251968384,
|
||||||
|
0.9982636570930481,
|
||||||
|
0.9973306059837341,
|
||||||
|
0.9959976673126221,
|
||||||
|
0.9947634339332581,
|
||||||
|
0.9936885833740234,
|
||||||
|
0.9931045770645142,
|
||||||
|
0.9929555654525757,
|
||||||
|
0.9927845597267151,
|
||||||
|
0.9922730922698975,
|
||||||
|
0.9920589923858643,
|
||||||
|
0.9921906590461731,
|
||||||
|
0.9921801686286926,
|
||||||
|
0.9919214844703674,
|
||||||
|
0.9921574592590332,
|
||||||
|
0.991858959197998,
|
||||||
|
0.9914454221725464,
|
||||||
|
0.9906716346740723,
|
||||||
|
0.9895113706588745,
|
||||||
|
0.9881779551506042,
|
||||||
|
0.9870789051055908,
|
||||||
|
0.9856075048446655,
|
||||||
|
0.9844176173210144,
|
||||||
|
0.9831746220588684,
|
||||||
|
0.9819408059120178,
|
||||||
|
0.9820376634597778,
|
||||||
|
0.9804425239562988,
|
||||||
|
0.9787063002586365,
|
||||||
|
0.9777268171310425,
|
||||||
|
0.9769911766052246,
|
||||||
|
0.9757769703865051,
|
||||||
|
0.9740085601806641,
|
||||||
|
0.9716024398803711,
|
||||||
|
0.9696326851844788,
|
||||||
|
0.9672284722328186,
|
||||||
|
0.9658068418502808,
|
||||||
|
0.9645152688026428,
|
||||||
|
0.9632120132446289,
|
||||||
|
0.962562084197998,
|
||||||
|
0.961824893951416,
|
||||||
|
0.9614293575286865,
|
||||||
|
0.9618523716926575,
|
||||||
|
0.9624170064926147,
|
||||||
|
0.9620550870895386,
|
||||||
|
0.9612102508544922,
|
||||||
|
0.9611724615097046,
|
||||||
|
0.9607089161872864,
|
||||||
|
0.9598875045776367,
|
||||||
|
0.9586215019226074,
|
||||||
|
0.9580100178718567,
|
||||||
|
0.9569817185401917,
|
||||||
|
0.9565159678459167,
|
||||||
|
0.9558516144752502,
|
||||||
|
0.954704999923706,
|
||||||
|
0.9524244666099548,
|
||||||
|
0.9503999948501587,
|
||||||
|
0.9483119249343872,
|
||||||
|
0.9459860920906067,
|
||||||
|
0.94413161277771
|
||||||
|
],
|
||||||
|
"gnorm_traj": [
|
||||||
|
0.023208482504073005,
|
||||||
|
0.02123692131703687,
|
||||||
|
0.021419118791516166,
|
||||||
|
0.019711600127132092,
|
||||||
|
0.019135414989471005,
|
||||||
|
0.018076975427293435,
|
||||||
|
0.016569579518674167,
|
||||||
|
0.01574714791728605,
|
||||||
|
0.015854544001130864,
|
||||||
|
0.01491067020364289,
|
||||||
|
0.01526323321636773,
|
||||||
|
0.014624104052628336,
|
||||||
|
0.01524563859595063,
|
||||||
|
0.014243056657597302,
|
||||||
|
0.0148277412824214,
|
||||||
|
0.014620955383358293,
|
||||||
|
0.014144884905931217,
|
||||||
|
0.013968142664540066,
|
||||||
|
0.014892411399669594,
|
||||||
|
0.013738916719669652,
|
||||||
|
0.013639220576268603,
|
||||||
|
0.013447892285315104,
|
||||||
|
0.013934486859842426,
|
||||||
|
0.014332657373375508,
|
||||||
|
0.014262476584197584,
|
||||||
|
0.013643783498035593,
|
||||||
|
0.01377261190373069,
|
||||||
|
0.013472539945253405,
|
||||||
|
0.013203984465352907,
|
||||||
|
0.01383900052756837,
|
||||||
|
0.01331712212948163,
|
||||||
|
0.013298554708660674,
|
||||||
|
0.013641285885523019,
|
||||||
|
0.013616118450480434,
|
||||||
|
0.013036425940213387,
|
||||||
|
0.013431248289146479,
|
||||||
|
0.013511816716475708,
|
||||||
|
0.014017017260614243,
|
||||||
|
0.012900150368019506,
|
||||||
|
0.013370100259288963,
|
||||||
|
0.013318981910991245,
|
||||||
|
0.0137673036535571,
|
||||||
|
0.013798510320460793,
|
||||||
|
0.013403432306579973,
|
||||||
|
0.013440239522431556,
|
||||||
|
0.01281450173493988,
|
||||||
|
0.012772421311467318,
|
||||||
|
0.013666647215714567,
|
||||||
|
0.01275326116724871,
|
||||||
|
0.013338132260418857,
|
||||||
|
0.012951403458113723,
|
||||||
|
0.013345290154289981,
|
||||||
|
0.013155987746513179,
|
||||||
|
0.013130926359220358,
|
||||||
|
0.01290534474999044,
|
||||||
|
0.012931056238603184,
|
||||||
|
0.013659869466568731,
|
||||||
|
0.012398440619775996,
|
||||||
|
0.012929384856787686,
|
||||||
|
0.012931776398032845
|
||||||
|
],
|
||||||
|
"eta_final": 0.9424571394920349,
|
||||||
|
"eta_max": 1.0010368824005127,
|
||||||
|
"gnorm_max": 0.024477812000968838
|
||||||
|
}
|
||||||
Executable
+107
@@ -0,0 +1,107 @@
|
|||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+185
@@ -0,0 +1,185 @@
|
|||||||
|
{
|
||||||
|
"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
|
||||||
|
],
|
||||||
|
"retr": [
|
||||||
|
0.22747395833333334,
|
||||||
|
0.29614583333333333,
|
||||||
|
0.36640625,
|
||||||
|
0.4305729166666667,
|
||||||
|
0.4890625,
|
||||||
|
0.5364322916666666,
|
||||||
|
0.5783854166666667,
|
||||||
|
0.6016145833333333,
|
||||||
|
0.6203125,
|
||||||
|
0.6344270833333333,
|
||||||
|
0.642265625
|
||||||
|
],
|
||||||
|
"erank": 63.314430236816406
|
||||||
|
},
|
||||||
|
"e2e_moving": {
|
||||||
|
"ser": [
|
||||||
|
0.9998697916666667,
|
||||||
|
0.99984375,
|
||||||
|
0.9999479166666667,
|
||||||
|
0.9999479166666667,
|
||||||
|
0.9999479166666667,
|
||||||
|
0.9998958333333333,
|
||||||
|
0.9999739583333334,
|
||||||
|
0.9998958333333333,
|
||||||
|
0.9999479166666667,
|
||||||
|
0.9998958333333333,
|
||||||
|
0.9999739583333334
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
-0.0022268955471614997,
|
||||||
|
0.001013178266584873,
|
||||||
|
0.0013794303367224833,
|
||||||
|
0.001468668068603923,
|
||||||
|
-0.005597147869411856,
|
||||||
|
-0.0026633564918302,
|
||||||
|
-0.0047864368790760635,
|
||||||
|
0.01127757717234393,
|
||||||
|
-0.0007127511920407415,
|
||||||
|
0.00015094189438968897,
|
||||||
|
-0.006568640521727502
|
||||||
|
],
|
||||||
|
"retr": [
|
||||||
|
0.0040885416666666665,
|
||||||
|
0.0040625,
|
||||||
|
0.004244791666666667,
|
||||||
|
0.004479166666666667,
|
||||||
|
0.004401041666666667,
|
||||||
|
0.0050260416666666665,
|
||||||
|
0.004895833333333334,
|
||||||
|
0.005052083333333333,
|
||||||
|
0.005078125,
|
||||||
|
0.005260416666666667,
|
||||||
|
0.004609375
|
||||||
|
],
|
||||||
|
"erank": 41.477378845214844
|
||||||
|
},
|
||||||
|
"e2e_anchored": {
|
||||||
|
"ser": [
|
||||||
|
0.8076822916666667,
|
||||||
|
0.7096614583333334,
|
||||||
|
0.600859375,
|
||||||
|
0.4913020833333333,
|
||||||
|
0.39229166666666665,
|
||||||
|
0.31416666666666665,
|
||||||
|
0.25463541666666667,
|
||||||
|
0.20625,
|
||||||
|
0.17880208333333333,
|
||||||
|
0.15955729166666666,
|
||||||
|
0.15265625
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.3334804524978002,
|
||||||
|
0.37222714444001515,
|
||||||
|
0.40820392191410065,
|
||||||
|
0.43878923694292704,
|
||||||
|
0.4647983853022257,
|
||||||
|
0.48393498480319974,
|
||||||
|
0.4995024112860362,
|
||||||
|
0.5123437927166621,
|
||||||
|
0.519522678454717,
|
||||||
|
0.5258413887023926,
|
||||||
|
0.5276070324579875
|
||||||
|
],
|
||||||
|
"retr": [
|
||||||
|
0.23111979166666666,
|
||||||
|
0.2997916666666667,
|
||||||
|
0.3709635416666667,
|
||||||
|
0.43872395833333333,
|
||||||
|
0.49971354166666665,
|
||||||
|
0.5374479166666667,
|
||||||
|
0.573671875,
|
||||||
|
0.6006510416666667,
|
||||||
|
0.61859375,
|
||||||
|
0.63234375,
|
||||||
|
0.64328125
|
||||||
|
],
|
||||||
|
"erank": 59.58533477783203
|
||||||
|
},
|
||||||
|
"e2e_vicreg": {
|
||||||
|
"ser": [
|
||||||
|
0.9999479166666667,
|
||||||
|
0.9998958333333333,
|
||||||
|
0.9998697916666667,
|
||||||
|
0.99984375,
|
||||||
|
0.9999479166666667,
|
||||||
|
0.9998958333333333,
|
||||||
|
0.999921875,
|
||||||
|
0.9999739583333334,
|
||||||
|
0.9999479166666667,
|
||||||
|
0.999921875,
|
||||||
|
0.9999739583333334
|
||||||
|
],
|
||||||
|
"cos": [
|
||||||
|
0.003705161992305269,
|
||||||
|
-0.0008268103026784956,
|
||||||
|
0.0036043038405478,
|
||||||
|
0.0009314771206118166,
|
||||||
|
-0.0039000791516931106,
|
||||||
|
0.0005898531658264498,
|
||||||
|
0.004959443456803759,
|
||||||
|
0.0029063680950397004,
|
||||||
|
-0.001753986528298507,
|
||||||
|
-4.444453322018186e-06,
|
||||||
|
0.002719290911530455
|
||||||
|
],
|
||||||
|
"retr": [
|
||||||
|
0.235546875,
|
||||||
|
0.30703125,
|
||||||
|
0.36395833333333333,
|
||||||
|
0.41716145833333335,
|
||||||
|
0.4685416666666667,
|
||||||
|
0.5091145833333334,
|
||||||
|
0.5313541666666667,
|
||||||
|
0.548046875,
|
||||||
|
0.560234375,
|
||||||
|
0.5616145833333334,
|
||||||
|
0.5776302083333333
|
||||||
|
],
|
||||||
|
"erank": 32.96731185913086
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+126
@@ -0,0 +1,126 @@
|
|||||||
|
{
|
||||||
|
"trajectory": {
|
||||||
|
"frame": [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
10,
|
||||||
|
11,
|
||||||
|
12,
|
||||||
|
13,
|
||||||
|
14,
|
||||||
|
15,
|
||||||
|
16
|
||||||
|
],
|
||||||
|
"online_cos": [
|
||||||
|
0.17329631745815277,
|
||||||
|
0.19247668981552124,
|
||||||
|
0.17097969353199005,
|
||||||
|
0.17287176847457886,
|
||||||
|
0.16061195731163025,
|
||||||
|
0.17628976702690125,
|
||||||
|
0.16555088758468628,
|
||||||
|
0.165620356798172,
|
||||||
|
0.1830531358718872,
|
||||||
|
0.1789100617170334,
|
||||||
|
0.1605786234140396,
|
||||||
|
0.1683427095413208,
|
||||||
|
0.17294596135616302,
|
||||||
|
0.16041973233222961,
|
||||||
|
0.16993699967861176,
|
||||||
|
0.18056748807430267
|
||||||
|
],
|
||||||
|
"oracle_cos": [
|
||||||
|
0.16824987530708313,
|
||||||
|
0.18634122610092163,
|
||||||
|
0.16665780544281006,
|
||||||
|
0.17287176847457886,
|
||||||
|
0.16061195731163025,
|
||||||
|
0.17628976702690125,
|
||||||
|
0.16555088758468628,
|
||||||
|
0.165620356798172,
|
||||||
|
0.1830531358718872,
|
||||||
|
0.1789100617170334,
|
||||||
|
0.1605786234140396,
|
||||||
|
0.1683427095413208,
|
||||||
|
0.17294596135616302,
|
||||||
|
0.16041973233222961,
|
||||||
|
0.16993699967861176,
|
||||||
|
0.18056748807430267
|
||||||
|
],
|
||||||
|
"full_cos": [
|
||||||
|
0.17329631745815277,
|
||||||
|
0.19247668981552124,
|
||||||
|
0.17097969353199005,
|
||||||
|
0.17735423147678375,
|
||||||
|
0.16562017798423767,
|
||||||
|
0.1799776256084442,
|
||||||
|
0.16680437326431274,
|
||||||
|
0.1748659312725067,
|
||||||
|
0.18854433298110962,
|
||||||
|
0.18770724534988403,
|
||||||
|
0.16589391231536865,
|
||||||
|
0.1744101345539093,
|
||||||
|
0.17331872880458832,
|
||||||
|
0.1641751229763031,
|
||||||
|
0.17973534762859344,
|
||||||
|
0.18085896968841553
|
||||||
|
],
|
||||||
|
"beta_err": [
|
||||||
|
0.006034748163074255,
|
||||||
|
0.018806079402565956,
|
||||||
|
0.028256120160222054,
|
||||||
|
0.03790559619665146,
|
||||||
|
0.042267411947250366,
|
||||||
|
0.04524436220526695,
|
||||||
|
0.04682108759880066,
|
||||||
|
0.04713748022913933,
|
||||||
|
0.04687809944152832,
|
||||||
|
0.04725177213549614,
|
||||||
|
0.0471353679895401,
|
||||||
|
0.04652136564254761,
|
||||||
|
0.04400625079870224,
|
||||||
|
0.04685147479176521,
|
||||||
|
0.04681994765996933,
|
||||||
|
0.04754137992858887
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"timing": {
|
||||||
|
"U": [
|
||||||
|
8,
|
||||||
|
16,
|
||||||
|
32,
|
||||||
|
64,
|
||||||
|
128
|
||||||
|
],
|
||||||
|
"full_ms": [
|
||||||
|
0.31470264948438853,
|
||||||
|
0.2423646510578692,
|
||||||
|
0.45809974981239066,
|
||||||
|
1.5150976993027143,
|
||||||
|
5.4322567986673675
|
||||||
|
],
|
||||||
|
"topk_ms": [
|
||||||
|
0.2245275492896326,
|
||||||
|
0.2692323498195037,
|
||||||
|
0.40805765020195395,
|
||||||
|
1.6764128507929854,
|
||||||
|
5.739847800577991
|
||||||
|
],
|
||||||
|
"select_ms": [
|
||||||
|
0.017265090136788785,
|
||||||
|
0.018207559769507498,
|
||||||
|
0.018143650086130947,
|
||||||
|
0.039025599835440516,
|
||||||
|
0.019912720017600805
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"k": 4,
|
||||||
|
"warm_frames": 3
|
||||||
|
}
|
||||||
Executable
+54
@@ -0,0 +1,54 @@
|
|||||||
|
"""E1 — Degrees-of-freedom fairness (R1.10, R3.7).
|
||||||
|
|
||||||
|
Adds full-dimensional receivers on the SAME received signal:
|
||||||
|
- lmmse_blind : optimal linear receiver with cross-user correlation set to 0
|
||||||
|
(proves the d/U ceiling is fundamental to correlation-blind
|
||||||
|
processing, not an artifact of the OFDMA baseline)
|
||||||
|
- lmmse_genie : optimal linear receiver given the true relevance matrix
|
||||||
|
(genie-aided upper reference; UWCA should approach it)
|
||||||
|
- tdma_proj : orthogonal scheme with an arbitrary orthonormal projection
|
||||||
|
(proves any orthogonal partition is statistically identical
|
||||||
|
to coordinate masking for isotropic embeddings)
|
||||||
|
Also trains the UWCA decoder per scenario under the single-signal model and
|
||||||
|
saves checkpoints for reuse (E6).
|
||||||
|
"""
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
|
||||||
|
import lib
|
||||||
|
from lib import (SCENARIOS, SNR_GRID, UWCA, DEVICE, beta_matrix, block_masks,
|
||||||
|
eval_scheme, gen_embeddings, save_json, set_seed, train_multitask)
|
||||||
|
|
||||||
|
rng = set_seed(42)
|
||||||
|
d, U, H = 64, 4, 4
|
||||||
|
masks = block_masks(U, d)
|
||||||
|
tasks = [{"snr_db": float(s)} for s in np.arange(0, 21, 4)]
|
||||||
|
|
||||||
|
out = {"snr": SNR_GRID.tolist(), "scenarios": {}}
|
||||||
|
for scen_name in ["HIGH", "LOW", "MIX"]:
|
||||||
|
scen = SCENARIOS[scen_name]
|
||||||
|
B = beta_matrix(scen)
|
||||||
|
|
||||||
|
def gen(n, scen=scen):
|
||||||
|
return gen_embeddings(n, d, U, rng, scen).to(DEVICE)
|
||||||
|
|
||||||
|
model = UWCA(d, U, H).to(DEVICE)
|
||||||
|
train_multitask(model, gen, tasks, epochs=300, tag=f"E1-{scen_name}")
|
||||||
|
torch.save(model.state_dict(), lib.DATA / f"e1_uwca_{scen_name}.pt")
|
||||||
|
|
||||||
|
res = {}
|
||||||
|
rng_t = torch.Generator().manual_seed(1)
|
||||||
|
for scheme in ["uwca", "ofdma", "sfdma", "noma", "lmmse_blind",
|
||||||
|
"lmmse_genie", "tdma_proj"]:
|
||||||
|
sers, coss = [], []
|
||||||
|
for snr in SNR_GRID:
|
||||||
|
t = {"snr_db": float(snr)}
|
||||||
|
s, c = eval_scheme(scheme, gen, t, n_mc=200, model=model, B=B,
|
||||||
|
masks=masks, rng_t=rng_t)
|
||||||
|
sers.append(s); coss.append(c)
|
||||||
|
res[scheme] = {"ser": sers, "cos": coss}
|
||||||
|
print(f"[E1-{scen_name}] {scheme}: SER@10dB={sers[5]:.3f} "
|
||||||
|
f"cos@10dB={coss[5]:.3f}", flush=True)
|
||||||
|
out["scenarios"][scen_name] = res
|
||||||
|
|
||||||
|
save_json("e1_fair_baselines.json", out)
|
||||||
Executable
+129
@@ -0,0 +1,129 @@
|
|||||||
|
"""E2 — Full complex-baseband phase-error model with inter-user leakage (R1.1)
|
||||||
|
plus CSI amplitude-error robustness (R2.3).
|
||||||
|
|
||||||
|
Three evaluation models on HIGH:
|
||||||
|
scalar : real channel, per-user cos(dphi) attenuation only (old model)
|
||||||
|
complex-I : full complex superposition; decoder reads the in-phase rail only
|
||||||
|
complex-IQ: full complex superposition; decoder reads both rails (proposed)
|
||||||
|
Two trained decoders (phase-augmented training, sigma_phi ~ U[0,20] deg):
|
||||||
|
m_real (iq=False) and m_iq (iq=True).
|
||||||
|
Also evaluates a decoder trained at sigma_phi=0 to expose training mismatch,
|
||||||
|
and a CSI amplitude error sweep for SFDMA (divides by h) vs UWCA (no CSI).
|
||||||
|
"""
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
|
||||||
|
import lib
|
||||||
|
from lib import (SCENARIOS, UWCA, DEVICE, block_masks, channel, eval_scheme,
|
||||||
|
gen_embeddings, mean_cos, save_json, ser, set_seed,
|
||||||
|
train_multitask)
|
||||||
|
|
||||||
|
rng = set_seed(42)
|
||||||
|
d, U, H = 64, 4, 4
|
||||||
|
masks = block_masks(U, d)
|
||||||
|
scen = SCENARIOS["HIGH"]
|
||||||
|
|
||||||
|
|
||||||
|
def gen(n):
|
||||||
|
return gen_embeddings(n, d, U, rng, scen).to(DEVICE)
|
||||||
|
|
||||||
|
|
||||||
|
snrs = [0.0, 4.0, 8.0, 12.0, 16.0, 20.0]
|
||||||
|
rng_ph = np.random.default_rng(3)
|
||||||
|
|
||||||
|
|
||||||
|
def aug_tasks():
|
||||||
|
return [{"snr_db": s, "phase_sigma_deg": float(rng_ph.uniform(0, 20))}
|
||||||
|
for s in snrs]
|
||||||
|
|
||||||
|
|
||||||
|
class AugTaskList:
|
||||||
|
"""List-like view that resamples phase residuals each epoch."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._t = aug_tasks()
|
||||||
|
self._n = 0
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._t)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
self._n += 1
|
||||||
|
self._t = aug_tasks()
|
||||||
|
return iter(self._t)
|
||||||
|
|
||||||
|
|
||||||
|
m_real = UWCA(d, U, H, iq=False).to(DEVICE)
|
||||||
|
train_multitask(m_real, gen, AugTaskList(), epochs=300, tag="E2-real-aug")
|
||||||
|
m_iq = UWCA(d, U, H, iq=True).to(DEVICE)
|
||||||
|
train_multitask(m_iq, gen, AugTaskList(), epochs=300, tag="E2-iq-aug")
|
||||||
|
m_zero = UWCA(d, U, H, iq=False).to(DEVICE)
|
||||||
|
train_multitask(m_zero, gen, [{"snr_db": s} for s in snrs], epochs=300,
|
||||||
|
tag="E2-zerophase")
|
||||||
|
|
||||||
|
torch.save(m_iq.state_dict(), lib.DATA / "e2_uwca_iq.pt")
|
||||||
|
|
||||||
|
sig_grid = [0, 5, 10, 15, 20, 30]
|
||||||
|
out = {"sigma_phi_deg": sig_grid, "snr_eval": [10.0, 20.0], "curves": {}}
|
||||||
|
|
||||||
|
|
||||||
|
@torch.no_grad()
|
||||||
|
def run(model, sig, snr, mode):
|
||||||
|
s_acc = c_acc = 0.0
|
||||||
|
n_mc = 200
|
||||||
|
for _ in range(n_mc):
|
||||||
|
E = gen(64)
|
||||||
|
if mode == "scalar":
|
||||||
|
# magnitude attenuation only: fold cos(dphi) into the gain, Q rail ignored
|
||||||
|
ch = channel(E, snr_db=snr, phase_sigma_deg=sig)
|
||||||
|
# scalar model == complex-I when masks are disjoint; emulate the
|
||||||
|
# old analytic model by discarding the Q rail entirely
|
||||||
|
Eh = model(ch["yI"], torch.zeros_like(ch["yQ"]))
|
||||||
|
elif mode == "cI":
|
||||||
|
ch = channel(E, snr_db=snr, phase_sigma_deg=sig)
|
||||||
|
Eh = model(ch["yI"], torch.zeros_like(ch["yQ"])) \
|
||||||
|
if not model.iq else model(ch["yI"], ch["yQ"])
|
||||||
|
elif mode == "cIQ":
|
||||||
|
ch = channel(E, snr_db=snr, phase_sigma_deg=sig)
|
||||||
|
Eh = model(ch["yI"], ch["yQ"])
|
||||||
|
s_acc += ser(Eh, E)
|
||||||
|
c_acc += mean_cos(Eh, E)
|
||||||
|
return s_acc / n_mc, c_acc / n_mc
|
||||||
|
|
||||||
|
|
||||||
|
for label, model, mode in [("scalar_augtrain", m_real, "scalar"),
|
||||||
|
("complexI_augtrain", m_real, "cI"),
|
||||||
|
("complexIQ_iqtrain", m_iq, "cIQ"),
|
||||||
|
("complexI_zerotrain", m_zero, "cI")]:
|
||||||
|
cur = {}
|
||||||
|
for snr in out["snr_eval"]:
|
||||||
|
cur[str(snr)] = {"ser": [], "cos": []}
|
||||||
|
for sig in sig_grid:
|
||||||
|
s, c = run(model, sig, snr, mode)
|
||||||
|
cur[str(snr)]["ser"].append(s)
|
||||||
|
cur[str(snr)]["cos"].append(c)
|
||||||
|
print(f"[E2] {label} snr={snr}: SER={cur[str(snr)]['ser']}", flush=True)
|
||||||
|
out["curves"][label] = cur
|
||||||
|
|
||||||
|
# soft-mask overlap of the trained decoders (quantifies the IUI channel)
|
||||||
|
with torch.no_grad():
|
||||||
|
for label, model in [("m_real", m_real), ("m_iq", m_iq)]:
|
||||||
|
m = model.soft_masks()
|
||||||
|
ov = (m @ m.T) / (m.norm(dim=1, keepdim=True) * m.norm(dim=1) + 1e-9)
|
||||||
|
off = ov[~torch.eye(U, dtype=torch.bool, device=ov.device)]
|
||||||
|
out[f"mask_overlap_{label}"] = {"mean": float(off.mean()),
|
||||||
|
"max": float(off.max())}
|
||||||
|
|
||||||
|
# CSI amplitude error: SFDMA (uses h) vs UWCA (no explicit CSI), sigma_phi=10
|
||||||
|
h_grid = [0.0, 0.05, 0.1, 0.2]
|
||||||
|
csi = {"h_err": h_grid, "uwca_ser": [], "sfdma_ser": []}
|
||||||
|
for he in h_grid:
|
||||||
|
t = {"snr_db": 10.0, "phase_sigma_deg": 10.0, "h_err_sigma": he}
|
||||||
|
s_u, _ = eval_scheme("uwca", gen, t, n_mc=200, model=m_iq, masks=masks)
|
||||||
|
s_f, _ = eval_scheme("sfdma", gen, t, n_mc=200, masks=masks)
|
||||||
|
csi["uwca_ser"].append(s_u)
|
||||||
|
csi["sfdma_ser"].append(s_f)
|
||||||
|
print(f"[E2-CSI] h_err={he}: UWCA {s_u:.3f} SFDMA {s_f:.3f}", flush=True)
|
||||||
|
out["csi_error"] = csi
|
||||||
|
|
||||||
|
save_json("e2_phase_iui.json", out)
|
||||||
Executable
+99
@@ -0,0 +1,99 @@
|
|||||||
|
"""E3 — Dynamic user arrivals/departures (R1.6, R2.3).
|
||||||
|
|
||||||
|
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
|
||||||
|
the active set announced by the scheduler (no retraining).
|
||||||
|
Compared against: (i) a model trained with all 8 users always active
|
||||||
|
(mismatch), and (ii) oracle models retrained for each fixed active count.
|
||||||
|
"""
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
|
||||||
|
import lib
|
||||||
|
from lib import (SCENARIOS, UWCA, DEVICE, block_masks, channel, gen_embeddings,
|
||||||
|
mean_cos, save_json, semantic_loss, ser, set_seed)
|
||||||
|
|
||||||
|
rng = set_seed(42)
|
||||||
|
d, Umax, H = 64, 8, 4
|
||||||
|
masks = block_masks(Umax, d)
|
||||||
|
scen = SCENARIOS["HIGH8"]
|
||||||
|
snrs = [0.0, 4.0, 8.0, 12.0, 16.0, 20.0]
|
||||||
|
act_rng = np.random.default_rng(11)
|
||||||
|
|
||||||
|
|
||||||
|
def gen(n):
|
||||||
|
return gen_embeddings(n, d, Umax, rng, scen).to(DEVICE)
|
||||||
|
|
||||||
|
|
||||||
|
def sample_active(n, k=None):
|
||||||
|
"""(n, Umax) bool with k active users (random subset per sample)."""
|
||||||
|
A = np.zeros((n, Umax), dtype=bool)
|
||||||
|
for i in range(n):
|
||||||
|
kk = k if k is not None else int(act_rng.integers(2, Umax + 1))
|
||||||
|
A[i, act_rng.choice(Umax, size=kk, replace=False)] = True
|
||||||
|
return torch.from_numpy(A).to(DEVICE)
|
||||||
|
|
||||||
|
|
||||||
|
def train(model, epochs=250, k=None, tag=""):
|
||||||
|
mask_p = [p for nm, p in model.named_parameters() if "mask_logits" in nm]
|
||||||
|
other = [p for nm, p in model.named_parameters() if "mask_logits" not in nm]
|
||||||
|
opt = torch.optim.Adam([{"params": other, "lr": 1e-3},
|
||||||
|
{"params": mask_p, "lr": 0.1}])
|
||||||
|
for ep in range(1, epochs + 1):
|
||||||
|
opt.zero_grad()
|
||||||
|
loss = 0.0
|
||||||
|
for s in snrs:
|
||||||
|
E = gen(64)
|
||||||
|
act = sample_active(64, k)
|
||||||
|
Ez = E * act[:, :, None] # inactive users transmit nothing
|
||||||
|
ch = channel(Ez, snr_db=s)
|
||||||
|
Eh = model(ch["yI"], ch["yQ"], active=act)
|
||||||
|
loss = loss + semantic_loss(Eh, E, 0.1, active=act.float())
|
||||||
|
(loss / len(snrs)).backward()
|
||||||
|
torch.nn.utils.clip_grad_norm_(model.parameters(), 5.0)
|
||||||
|
opt.step()
|
||||||
|
if ep % 50 == 0:
|
||||||
|
print(f" [E3-{tag}] ep {ep}/{epochs} loss={float(loss)/len(snrs):.4f}",
|
||||||
|
flush=True)
|
||||||
|
|
||||||
|
|
||||||
|
@torch.no_grad()
|
||||||
|
def evaluate(model, k, snr, n_mc=200):
|
||||||
|
s_acc = c_acc = 0.0
|
||||||
|
for _ in range(n_mc):
|
||||||
|
E = gen(64)
|
||||||
|
act = sample_active(64, k)
|
||||||
|
Ez = E * act[:, :, None]
|
||||||
|
ch = channel(Ez, snr_db=snr)
|
||||||
|
Eh = model(ch["yI"], ch["yQ"], active=act)
|
||||||
|
s_acc += ser(Eh, E, active=act.float())
|
||||||
|
c_acc += mean_cos(Eh, E, active=act.float())
|
||||||
|
return s_acc / n_mc, c_acc / n_mc
|
||||||
|
|
||||||
|
|
||||||
|
print("[E3] training activity-aware model", flush=True)
|
||||||
|
m_act = UWCA(d, Umax, H).to(DEVICE)
|
||||||
|
train(m_act, tag="act")
|
||||||
|
torch.save(m_act.state_dict(), lib.DATA / "e3_uwca_act.pt")
|
||||||
|
|
||||||
|
print("[E3] training fixed-U8 model", flush=True)
|
||||||
|
m_fix = UWCA(d, Umax, H).to(DEVICE)
|
||||||
|
train(m_fix, k=Umax, tag="fix8")
|
||||||
|
|
||||||
|
ks = [2, 3, 4, 5, 6, 7, 8]
|
||||||
|
out = {"k": ks, "snr_eval": [10.0, 20.0], "activity": {}, "fixed8": {},
|
||||||
|
"oracle": {}}
|
||||||
|
for snr in out["snr_eval"]:
|
||||||
|
out["activity"][str(snr)] = [evaluate(m_act, k, snr) for k in ks]
|
||||||
|
out["fixed8"][str(snr)] = [evaluate(m_fix, k, snr) for k in ks]
|
||||||
|
print(f"[E3] snr={snr} activity={[f'{a[0]:.3f}' for a in out['activity'][str(snr)]]}",
|
||||||
|
flush=True)
|
||||||
|
|
||||||
|
for k in [2, 4, 6, 8]:
|
||||||
|
m_o = UWCA(d, Umax, H).to(DEVICE)
|
||||||
|
train(m_o, k=k, epochs=250, tag=f"oracle{k}")
|
||||||
|
out["oracle"][str(k)] = {str(snr): evaluate(m_o, k, snr)
|
||||||
|
for snr in out["snr_eval"]}
|
||||||
|
print(f"[E3] oracle k={k}: {out['oracle'][str(k)]}", flush=True)
|
||||||
|
|
||||||
|
save_json("e3_dynamic_users.json", out)
|
||||||
Executable
+94
@@ -0,0 +1,94 @@
|
|||||||
|
"""E4 v3 — Timing offsets with BLOCK-WISE receiver realignment (R1.8).
|
||||||
|
|
||||||
|
The BS knows the per-user timing estimates (pilot-based) and realigns each
|
||||||
|
user's block region individually inside the single received frame:
|
||||||
|
y_al[b_w + t] = y[b_w + t + delta_w], t = 0..d/U-1
|
||||||
|
Energy that crossed block boundaries is lost or appears as residual
|
||||||
|
interference, exactly as in a real system with per-user timing advance error.
|
||||||
|
"""
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
import torch.nn.functional as F
|
||||||
|
|
||||||
|
import lib
|
||||||
|
from lib import (SCENARIOS, UWCA, DEVICE, block_masks, channel, gen_embeddings,
|
||||||
|
mean_cos, ofdma_decode, save_json, ser, set_seed)
|
||||||
|
|
||||||
|
rng = set_seed(42)
|
||||||
|
d, U, H = 64, 4, 4
|
||||||
|
dpu = d // U
|
||||||
|
masks = block_masks(U, d)
|
||||||
|
scen = SCENARIOS["HIGH"]
|
||||||
|
off_rng = np.random.default_rng(5)
|
||||||
|
|
||||||
|
|
||||||
|
def gen(n):
|
||||||
|
return gen_embeddings(n, d, U, rng, scen).to(DEVICE)
|
||||||
|
|
||||||
|
|
||||||
|
model = UWCA(d, U, H).to(DEVICE)
|
||||||
|
model.load_state_dict(torch.load(lib.DATA / "e1_uwca_HIGH.pt",
|
||||||
|
map_location=DEVICE))
|
||||||
|
model.eval()
|
||||||
|
|
||||||
|
|
||||||
|
def realign(y, offs_hat):
|
||||||
|
"""Block-wise realignment: shift each user's block back by its offset."""
|
||||||
|
n, dd = y.shape
|
||||||
|
out = torch.zeros_like(y)
|
||||||
|
for u in range(U):
|
||||||
|
b0 = u * dpu
|
||||||
|
for o in offs_hat[:, u].unique():
|
||||||
|
o = int(o.item())
|
||||||
|
idx = offs_hat[:, u] == o
|
||||||
|
src_end = min(b0 + dpu + o, dd)
|
||||||
|
ln = src_end - (b0 + o)
|
||||||
|
if ln > 0:
|
||||||
|
out[idx, b0:b0 + ln] = y[idx, b0 + o:src_end]
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
@torch.no_grad()
|
||||||
|
def evaluate(mode, dmax, snr, err_p=0.0, n_mc=200):
|
||||||
|
s_acc = c_acc = 0.0
|
||||||
|
for _ in range(n_mc):
|
||||||
|
E = gen(64)
|
||||||
|
offs = None
|
||||||
|
if dmax > 0:
|
||||||
|
offs = torch.from_numpy(
|
||||||
|
off_rng.integers(0, dmax + 1, size=(64, U))).to(DEVICE)
|
||||||
|
ch = channel(E, snr_db=snr, offsets=offs)
|
||||||
|
y = ch["yI"]
|
||||||
|
if dmax > 0 and mode.endswith("cor"):
|
||||||
|
offs_hat = offs.clone()
|
||||||
|
if err_p > 0:
|
||||||
|
flip = torch.from_numpy(
|
||||||
|
off_rng.random((64, U)) < err_p).to(DEVICE)
|
||||||
|
pm = torch.from_numpy(
|
||||||
|
off_rng.choice([-1, 1], size=(64, U))).to(DEVICE)
|
||||||
|
offs_hat = (offs_hat + flip.long() * pm).clamp(min=0)
|
||||||
|
y = realign(y, offs_hat)
|
||||||
|
if mode.startswith("uwca"):
|
||||||
|
Eh = model(y, ch["yQ"])
|
||||||
|
else:
|
||||||
|
Eh = ofdma_decode(y, masks)
|
||||||
|
s_acc += ser(Eh, E)
|
||||||
|
c_acc += mean_cos(Eh, E)
|
||||||
|
return s_acc / n_mc, c_acc / n_mc
|
||||||
|
|
||||||
|
|
||||||
|
dgrid = [0, 1, 2, 4, 8]
|
||||||
|
out = {"dmax": dgrid, "snr_eval": [10.0, 20.0], "curves": {}}
|
||||||
|
for label, mode, ep in [("uwca_uncorrected", "uwca_unc", 0.0),
|
||||||
|
("ofdma_uncorrected", "ofdma_unc", 0.0),
|
||||||
|
("uwca_corrected", "uwca_cor", 0.0),
|
||||||
|
("ofdma_corrected", "ofdma_cor", 0.0),
|
||||||
|
("uwca_corrected_err20", "uwca_cor", 0.2)]:
|
||||||
|
cur = {}
|
||||||
|
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: "
|
||||||
|
f"{[round(a[0],3) for a in cur['10.0']]}", flush=True)
|
||||||
|
|
||||||
|
save_json("e4_v3_async.json", out)
|
||||||
Executable
+55
@@ -0,0 +1,55 @@
|
|||||||
|
"""E5 — Nonlinear inter-user semantic structure (R1.7, R2.5, R3.2).
|
||||||
|
|
||||||
|
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
|
||||||
|
nonlinear and NOT captured by any scalar coefficient or linear covariance.
|
||||||
|
NONLIN-HIGH: shared s (kappa=1); NONLIN-LOW: independent s per user.
|
||||||
|
Shows the trained UWCA decoder still exploits the shared structure while the
|
||||||
|
scalar-parameterized genie LMMSE (mis-specified here) cannot fully.
|
||||||
|
"""
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
|
||||||
|
import lib
|
||||||
|
from lib import (UWCA, DEVICE, ViewNets, block_masks, eval_scheme, save_json,
|
||||||
|
set_seed, train_multitask, SNR_GRID)
|
||||||
|
|
||||||
|
rng = set_seed(42)
|
||||||
|
d, U, H = 64, 4, 4
|
||||||
|
masks = block_masks(U, d)
|
||||||
|
vnets = ViewNets(d, U).to(DEVICE)
|
||||||
|
tasks = [{"snr_db": float(s)} for s in np.arange(0, 21, 4)]
|
||||||
|
|
||||||
|
out = {"snr": SNR_GRID.tolist(), "cases": {}}
|
||||||
|
for case, (kappa, shared) in {"NONLIN-HIGH": (1.0, True),
|
||||||
|
"NONLIN-LOW": (1.0, False)}.items():
|
||||||
|
def gen(n, kappa=kappa, shared=shared):
|
||||||
|
return vnets.gen(n, d, U, rng, kappa, shared)
|
||||||
|
|
||||||
|
# empirical mean pairwise cosine (the "effective" relevance)
|
||||||
|
E = gen(2048)
|
||||||
|
C = torch.einsum("nud,nvd->uv", E, E) / 2048
|
||||||
|
off = C[~torch.eye(U, dtype=torch.bool, device=C.device)]
|
||||||
|
beta_emp = float(off.mean())
|
||||||
|
print(f"[E5-{case}] empirical mean pairwise cosine = {beta_emp:.3f}",
|
||||||
|
flush=True)
|
||||||
|
|
||||||
|
# mis-specified scalar-model LMMSE uses beta_emp for every pair
|
||||||
|
B = np.full((U, U), beta_emp)
|
||||||
|
np.fill_diagonal(B, 1.0)
|
||||||
|
|
||||||
|
model = UWCA(d, U, H).to(DEVICE)
|
||||||
|
train_multitask(model, gen, tasks, epochs=300, tag=f"E5-{case}")
|
||||||
|
|
||||||
|
res = {"beta_emp": beta_emp}
|
||||||
|
for scheme in ["uwca", "ofdma", "noma", "lmmse_genie"]:
|
||||||
|
sers, coss = [], []
|
||||||
|
for snr in SNR_GRID:
|
||||||
|
s, c = eval_scheme(scheme, gen, {"snr_db": float(snr)}, n_mc=200,
|
||||||
|
model=model, B=B, masks=masks)
|
||||||
|
sers.append(s); coss.append(c)
|
||||||
|
res[scheme] = {"ser": sers, "cos": coss}
|
||||||
|
print(f"[E5-{case}] {scheme}: SER@10dB={sers[5]:.3f}", flush=True)
|
||||||
|
out["cases"][case] = res
|
||||||
|
|
||||||
|
save_json("e5_nonlinear.json", out)
|
||||||
Executable
+66
@@ -0,0 +1,66 @@
|
|||||||
|
"""E6 — Residual (error) orthogonality vs. content preservation (R1.4, R3.3, R2.2).
|
||||||
|
|
||||||
|
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
|
||||||
|
RESIDUALS r_u = e_hat_u - e_u decorrelate (rho(r_u, r_v) -> 0), which is the
|
||||||
|
interference-suppression property. Measured per-sample across dimensions on
|
||||||
|
the E1 HIGH-trained decoder, vs SNR, together with the scene-component cosine.
|
||||||
|
"""
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
|
||||||
|
import lib
|
||||||
|
from lib import (SCENARIOS, SNR_GRID, UWCA, DEVICE, beta_matrix, block_masks,
|
||||||
|
channel, gen_embeddings, ofdma_decode, sample_corr, save_json,
|
||||||
|
set_seed)
|
||||||
|
|
||||||
|
rng = set_seed(42)
|
||||||
|
d, U, H = 64, 4, 4
|
||||||
|
masks = block_masks(U, d)
|
||||||
|
scen = SCENARIOS["HIGH"]
|
||||||
|
B = beta_matrix(scen)
|
||||||
|
|
||||||
|
model = UWCA(d, U, H).to(DEVICE)
|
||||||
|
model.load_state_dict(torch.load(lib.DATA / "e1_uwca_HIGH.pt",
|
||||||
|
map_location=DEVICE))
|
||||||
|
model.eval()
|
||||||
|
|
||||||
|
pairs = [(u, v) for u in range(U) for v in range(u + 1, U)]
|
||||||
|
out = {"snr": SNR_GRID.tolist(), "beta_uv_mean": float(np.mean(
|
||||||
|
[B[u, v] for u, v in pairs])), "uwca": {}, "ofdma": {}}
|
||||||
|
|
||||||
|
|
||||||
|
@torch.no_grad()
|
||||||
|
def measure(decode_fn):
|
||||||
|
rho_in, rho_out, rho_res = [], [], []
|
||||||
|
for snr in SNR_GRID:
|
||||||
|
a_in = a_out = a_res = 0.0
|
||||||
|
n_mc = 100
|
||||||
|
for _ in range(n_mc):
|
||||||
|
E = gen_embeddings(64, d, U, rng, scen).to(DEVICE)
|
||||||
|
ch = channel(E, snr_db=float(snr))
|
||||||
|
Eh = decode_fn(ch)
|
||||||
|
R = Eh - E
|
||||||
|
pi = po = pr = 0.0
|
||||||
|
for u, v in pairs:
|
||||||
|
pi += sample_corr(E[:, u], E[:, v])
|
||||||
|
po += sample_corr(Eh[:, u], Eh[:, v])
|
||||||
|
pr += sample_corr(R[:, u], R[:, v])
|
||||||
|
a_in += pi / len(pairs)
|
||||||
|
a_out += po / len(pairs)
|
||||||
|
a_res += pr / len(pairs)
|
||||||
|
rho_in.append(a_in / n_mc)
|
||||||
|
rho_out.append(a_out / n_mc)
|
||||||
|
rho_res.append(a_res / n_mc)
|
||||||
|
return rho_in, rho_out, rho_res
|
||||||
|
|
||||||
|
|
||||||
|
ri, ro, rr = measure(lambda ch: model(ch["yI"], ch["yQ"]))
|
||||||
|
out["uwca"] = {"rho_input": ri, "rho_decoded": ro, "rho_residual": rr}
|
||||||
|
print(f"[E6] UWCA rho_in={ri[5]:.3f} rho_dec={ro[5]:.3f} rho_res={rr[5]:.3f} @10dB",
|
||||||
|
flush=True)
|
||||||
|
|
||||||
|
ri, ro, rr = measure(lambda ch: ofdma_decode(ch["yI"], masks))
|
||||||
|
out["ofdma"] = {"rho_input": ri, "rho_decoded": ro, "rho_residual": rr}
|
||||||
|
|
||||||
|
save_json("e6_residual_orth.json", out)
|
||||||
Executable
+93
@@ -0,0 +1,93 @@
|
|||||||
|
"""E7 v2 — Meta-training over the multi-dimensional task family, OOD 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})
|
||||||
|
lookup : per-SNR specialists trained on Rayleigh / no phase error, indexed by
|
||||||
|
nearest SNR (the 1-D lookup table)
|
||||||
|
Test on held-out task combinations (incl. unseen Nakagami fading), zero-shot
|
||||||
|
and with S in {1,5,10,20} inner adaptation steps from each initialization.
|
||||||
|
"""
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
|
||||||
|
import lib
|
||||||
|
from lib import (SCENARIOS, UWCA, DEVICE, adapt, block_masks, eval_scheme,
|
||||||
|
gen_embeddings, save_json, set_seed, train_multitask)
|
||||||
|
|
||||||
|
rng = set_seed(42)
|
||||||
|
d, U, H = 64, 4, 4
|
||||||
|
masks = block_masks(U, d)
|
||||||
|
scen = SCENARIOS["HIGH"]
|
||||||
|
|
||||||
|
|
||||||
|
def gen(n):
|
||||||
|
return gen_embeddings(n, d, U, rng, scen).to(DEVICE)
|
||||||
|
|
||||||
|
|
||||||
|
snrs = [0.0, 4.0, 8.0, 12.0, 16.0, 20.0]
|
||||||
|
fads = [{"fading": "rayleigh"},
|
||||||
|
{"fading": "rician", "rician_K_dB": 5.0},
|
||||||
|
{"fading": "rician", "rician_K_dB": 10.0}]
|
||||||
|
phis = [0.0, 10.0]
|
||||||
|
family = [{"snr_db": s, "phase_sigma_deg": p, **f}
|
||||||
|
for s in snrs for f in fads for p in phis]
|
||||||
|
|
||||||
|
eta_log = []
|
||||||
|
m_meta = UWCA(d, U, H).to(DEVICE)
|
||||||
|
train_multitask(m_meta, gen, family, epochs=300, tag="E7v2-meta",
|
||||||
|
log_state=eta_log)
|
||||||
|
torch.save(m_meta.state_dict(), lib.DATA / "e7v2_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)}")
|
||||||
|
specialists[s] = m
|
||||||
|
|
||||||
|
|
||||||
|
def lookup(snr):
|
||||||
|
return specialists[min(snrs, key=lambda x: abs(x - snr))]
|
||||||
|
|
||||||
|
|
||||||
|
test_tasks = {
|
||||||
|
"ricianK20_phi15_snr10": {"snr_db": 10.0, "fading": "rician",
|
||||||
|
"rician_K_dB": 20.0, "phase_sigma_deg": 15.0},
|
||||||
|
"nakagami3_phi5_snr10": {"snr_db": 10.0, "fading": "nakagami",
|
||||||
|
"nakagami_m": 3.0, "phase_sigma_deg": 5.0},
|
||||||
|
"rayleigh_phi20_snr6": {"snr_db": 6.0, "fading": "rayleigh",
|
||||||
|
"phase_sigma_deg": 20.0},
|
||||||
|
"ricianK20_phi15_snr18": {"snr_db": 18.0, "fading": "rician",
|
||||||
|
"rician_K_dB": 20.0, "phase_sigma_deg": 15.0},
|
||||||
|
"indist_rayleigh_snr10": {"snr_db": 10.0, "fading": "rayleigh",
|
||||||
|
"phase_sigma_deg": 0.0},
|
||||||
|
}
|
||||||
|
|
||||||
|
S_grid = [0, 1, 5, 10, 20]
|
||||||
|
out = {"S_grid": S_grid, "results": {}}
|
||||||
|
for name, t in test_tasks.items():
|
||||||
|
row = {}
|
||||||
|
for label, base in [("meta", m_meta), ("lookup", lookup(t["snr_db"]))]:
|
||||||
|
sers = []
|
||||||
|
for S in S_grid:
|
||||||
|
mdl = base if S == 0 else adapt(base, gen, t, steps=S,
|
||||||
|
inner_lr=0.02)
|
||||||
|
s, c = eval_scheme("uwca", gen, t, n_mc=150, model=mdl,
|
||||||
|
masks=masks)
|
||||||
|
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']]} "
|
||||||
|
f"lookup={[round(x['ser'],3) for x in row['lookup']]}", flush=True)
|
||||||
|
|
||||||
|
etas = [e["eta"] for e in eta_log]
|
||||||
|
gns = [e["gnorm"] for e in eta_log]
|
||||||
|
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} "
|
||||||
|
f"gnorm max={max(gns):.3f}", flush=True)
|
||||||
|
|
||||||
|
save_json("e7_v2_meta.json", out)
|
||||||
Executable
+125
@@ -0,0 +1,125 @@
|
|||||||
|
"""E8 v2 — End-to-end joint encoder-decoder training (R1.5), source-anchored.
|
||||||
|
|
||||||
|
All fidelity metrics are measured against the SOURCE embedding normalize(x),
|
||||||
|
never against the trainable encoder output (a moving target that makes
|
||||||
|
collapse look like success).
|
||||||
|
|
||||||
|
Configs (HIGH):
|
||||||
|
frozen : identity encoder, decoder trained (paper reference)
|
||||||
|
e2e_moving : trainable encoder, loss vs f_phi(x) (collapse demo)
|
||||||
|
e2e_anchored : trainable encoder, loss vs normalize(x) (fixed anchor)
|
||||||
|
e2e_vicreg : trainable encoder, loss vs f_phi(x) + VICReg anti-collapse
|
||||||
|
Metrics: SER/cos vs normalize(x); batch nearest-neighbor retrieval accuracy;
|
||||||
|
encoder-output effective rank and off-diagonal correlation.
|
||||||
|
"""
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
import torch.nn.functional as F
|
||||||
|
|
||||||
|
import lib
|
||||||
|
from lib import (SCENARIOS, SNR_GRID, UWCA, DEVICE, Encoder, block_masks,
|
||||||
|
channel, gen_embeddings, save_json, semantic_loss, set_seed)
|
||||||
|
|
||||||
|
rng = set_seed(42)
|
||||||
|
d, U, H = 64, 4, 4
|
||||||
|
masks = block_masks(U, d)
|
||||||
|
scen = SCENARIOS["HIGH"]
|
||||||
|
snrs = [0.0, 4.0, 8.0, 12.0, 16.0, 20.0]
|
||||||
|
|
||||||
|
|
||||||
|
def gen(n):
|
||||||
|
return gen_embeddings(n, d, U, rng, scen).to(DEVICE)
|
||||||
|
|
||||||
|
|
||||||
|
def vicreg_reg(Z):
|
||||||
|
Zc = Z - Z.mean(0, keepdim=True)
|
||||||
|
std = (Zc.var(0) + 1e-4).sqrt()
|
||||||
|
v = F.relu(1.0 / d ** 0.5 - std).mean()
|
||||||
|
C = (Zc.T @ Zc) / (Z.shape[0] - 1)
|
||||||
|
off = C - torch.diag(torch.diag(C))
|
||||||
|
c = (off ** 2).sum() / d
|
||||||
|
return 25.0 * v + 100.0 * c
|
||||||
|
|
||||||
|
|
||||||
|
def train(mode, epochs=300):
|
||||||
|
model = UWCA(d, U, H).to(DEVICE)
|
||||||
|
enc = Encoder(d).to(DEVICE) if mode != "frozen" else None
|
||||||
|
mask_p = [p for nm, p in model.named_parameters() if "mask_logits" in nm]
|
||||||
|
other = [p for nm, p in model.named_parameters() if "mask_logits" not in nm]
|
||||||
|
groups = [{"params": other, "lr": 1e-3}, {"params": mask_p, "lr": 0.1}]
|
||||||
|
if enc is not None:
|
||||||
|
groups.append({"params": enc.parameters(), "lr": 1e-3})
|
||||||
|
opt = torch.optim.Adam(groups)
|
||||||
|
for ep in range(1, epochs + 1):
|
||||||
|
opt.zero_grad()
|
||||||
|
loss = 0.0
|
||||||
|
for s in snrs:
|
||||||
|
X = gen(64)
|
||||||
|
n = X.shape[0]
|
||||||
|
E = enc(X.reshape(-1, d)).reshape(n, U, d) if enc is not None else X
|
||||||
|
target = E if mode in ("frozen", "e2e_moving", "e2e_vicreg") else X
|
||||||
|
ch = channel(E, snr_db=s)
|
||||||
|
Eh = model(ch["yI"], ch["yQ"])
|
||||||
|
L = semantic_loss(Eh, target.detach()
|
||||||
|
if mode == "e2e_moving_detach" else target, 0.1)
|
||||||
|
if mode == "e2e_vicreg":
|
||||||
|
L = L + vicreg_reg(E.reshape(-1, d))
|
||||||
|
loss = loss + L
|
||||||
|
(loss / len(snrs)).backward()
|
||||||
|
torch.nn.utils.clip_grad_norm_(model.parameters(), 5.0)
|
||||||
|
opt.step()
|
||||||
|
if ep % 75 == 0:
|
||||||
|
print(f" [E8v2-{mode}] ep {ep}/{epochs} "
|
||||||
|
f"loss={float(loss)/len(snrs):.4f}", flush=True)
|
||||||
|
return model, enc
|
||||||
|
|
||||||
|
|
||||||
|
@torch.no_grad()
|
||||||
|
def collapse_metrics(enc):
|
||||||
|
X = gen(1024).reshape(-1, d)
|
||||||
|
Z = enc(X) if enc is not None else F.normalize(X, dim=-1)
|
||||||
|
Zc = Z - Z.mean(0, keepdim=True)
|
||||||
|
C = (Zc.T @ Zc) / (Z.shape[0] - 1)
|
||||||
|
ev = torch.linalg.eigvalsh(C).clamp(min=1e-12)
|
||||||
|
p = ev / ev.sum()
|
||||||
|
return float(torch.exp(-(p * p.log()).sum()))
|
||||||
|
|
||||||
|
|
||||||
|
@torch.no_grad()
|
||||||
|
def curves(model, enc):
|
||||||
|
ss, cc, rr = [], [], []
|
||||||
|
for snr in SNR_GRID:
|
||||||
|
a = b = r = 0.0
|
||||||
|
n_mc = 150
|
||||||
|
for _ in range(n_mc):
|
||||||
|
X = gen(64)
|
||||||
|
n = X.shape[0]
|
||||||
|
E = enc(X.reshape(-1, d)).reshape(n, U, d) if enc is not None else X
|
||||||
|
ch = channel(E, snr_db=float(snr))
|
||||||
|
Eh = model(ch["yI"], ch["yQ"])
|
||||||
|
# all metrics vs the SOURCE
|
||||||
|
cos = (Eh * X).sum(-1)
|
||||||
|
a += float((cos < 0.45).float().mean())
|
||||||
|
b += float(cos.mean())
|
||||||
|
# batch retrieval: nearest ENCODED gallery entry (collapse makes
|
||||||
|
# the gallery indistinguishable and drives accuracy to chance)
|
||||||
|
q = Eh.reshape(-1, d)
|
||||||
|
g = E.reshape(-1, d)
|
||||||
|
sim = q @ g.T
|
||||||
|
r += float((sim.argmax(1) == torch.arange(q.shape[0],
|
||||||
|
device=q.device))
|
||||||
|
.float().mean())
|
||||||
|
ss.append(a / n_mc); cc.append(b / n_mc); rr.append(r / n_mc)
|
||||||
|
return ss, cc, rr
|
||||||
|
|
||||||
|
|
||||||
|
out = {"snr": SNR_GRID.tolist(), "configs": {}}
|
||||||
|
for mode in ["frozen", "e2e_moving", "e2e_anchored", "e2e_vicreg"]:
|
||||||
|
model, enc = train(mode)
|
||||||
|
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} "
|
||||||
|
f"cos@10={cc[5]:.3f} retr@10={rr[5]:.3f}", flush=True)
|
||||||
|
|
||||||
|
save_json("e8_v2_e2e.json", out)
|
||||||
Executable
+144
@@ -0,0 +1,144 @@
|
|||||||
|
"""E9 — Online relevance acquisition for sparse top-k attention (R1.2, R3.6)
|
||||||
|
and measured selection/sorting overhead (R2.4).
|
||||||
|
|
||||||
|
Protocol (U=32, 8 clusters of 4, k=4):
|
||||||
|
frames 1..3 : full attention; the BS estimates beta_hat from the decoded
|
||||||
|
embeddings by an EWMA of pairwise cosines (no oracle knowledge)
|
||||||
|
frames >=4 : top-k attention using beta_hat (self + k-1 best peers)
|
||||||
|
Reports the per-frame fidelity trajectory against the oracle top-k (true
|
||||||
|
clusters) and full attention, plus wall-clock timing of the full pipeline
|
||||||
|
including estimation and argpartition selection for U in {8..128}.
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
|
||||||
|
import lib
|
||||||
|
from lib import (UWCA, DEVICE, block_masks, channel, gen_embeddings,
|
||||||
|
mean_cos, save_json, semantic_loss, ser, set_seed)
|
||||||
|
|
||||||
|
rng = set_seed(42)
|
||||||
|
d, H, k = 64, 4, 4
|
||||||
|
U = 32
|
||||||
|
G = U // k # 8 clusters of 4
|
||||||
|
scen = {"beta_u": [0.65] * U, "scenes": [i // k for i in range(U)]}
|
||||||
|
masks = block_masks(U, d)
|
||||||
|
snrs = [0.0, 10.0, 20.0]
|
||||||
|
|
||||||
|
|
||||||
|
def gen(n):
|
||||||
|
return gen_embeddings(n, d, U, rng, scen).to(DEVICE)
|
||||||
|
|
||||||
|
|
||||||
|
def cluster_mask():
|
||||||
|
m = torch.zeros(U, U, dtype=torch.bool, device=DEVICE)
|
||||||
|
for u in range(U):
|
||||||
|
c = u // k
|
||||||
|
m[u, c * k:(c + 1) * k] = True
|
||||||
|
return m
|
||||||
|
|
||||||
|
|
||||||
|
def topk_from_beta(bhat):
|
||||||
|
m = torch.zeros(U, U, dtype=torch.bool, device=DEVICE)
|
||||||
|
b = bhat.clone()
|
||||||
|
b.fill_diagonal_(2.0) # always keep self
|
||||||
|
idx = torch.topk(b, k, dim=1).indices
|
||||||
|
m.scatter_(1, idx, True)
|
||||||
|
return m
|
||||||
|
|
||||||
|
|
||||||
|
print("[E9] training U=32 model (full attention)", flush=True)
|
||||||
|
model = UWCA(d, U, H).to(DEVICE)
|
||||||
|
mask_p = [p for nm, p in model.named_parameters() if "mask_logits" in nm]
|
||||||
|
other = [p for nm, p in model.named_parameters() if "mask_logits" not in nm]
|
||||||
|
opt = torch.optim.Adam([{"params": other, "lr": 1e-3},
|
||||||
|
{"params": mask_p, "lr": 0.1}])
|
||||||
|
for ep in range(1, 181):
|
||||||
|
opt.zero_grad()
|
||||||
|
loss = 0.0
|
||||||
|
for s in snrs:
|
||||||
|
E = gen(64)
|
||||||
|
ch = channel(E, snr_db=s)
|
||||||
|
Eh = model(ch["yI"], ch["yQ"])
|
||||||
|
loss = loss + semantic_loss(Eh, E, 0.1)
|
||||||
|
(loss / len(snrs)).backward()
|
||||||
|
torch.nn.utils.clip_grad_norm_(model.parameters(), 5.0)
|
||||||
|
opt.step()
|
||||||
|
if ep % 45 == 0:
|
||||||
|
print(f" [E9] ep {ep}/180 loss={float(loss)/len(snrs):.4f}", flush=True)
|
||||||
|
|
||||||
|
# ---- online protocol trajectory at 10 dB
|
||||||
|
T, warm, gamma_ewma = 16, 3, 0.5
|
||||||
|
oracle = cluster_mask()
|
||||||
|
traj = {"frame": list(range(1, T + 1)), "online_cos": [], "oracle_cos": [],
|
||||||
|
"full_cos": [], "beta_err": []}
|
||||||
|
bhat = torch.zeros(U, U, device=DEVICE)
|
||||||
|
model.eval()
|
||||||
|
Btrue = torch.zeros(U, U, device=DEVICE)
|
||||||
|
for u in range(U):
|
||||||
|
for v in range(U):
|
||||||
|
if u != v and scen["scenes"][u] == scen["scenes"][v]:
|
||||||
|
Btrue[u, v] = scen["beta_u"][u] * scen["beta_u"][v]
|
||||||
|
|
||||||
|
with torch.no_grad():
|
||||||
|
for t in range(1, T + 1):
|
||||||
|
E = gen(256)
|
||||||
|
ch = channel(E, snr_db=10.0)
|
||||||
|
tk = None if t <= warm else topk_from_beta(bhat)
|
||||||
|
Eh = model(ch["yI"], ch["yQ"], topk_mask=tk)
|
||||||
|
# BS-side estimate from decoded embeddings only
|
||||||
|
Cb = torch.einsum("nud,nvd->uv", Eh, Eh) / Eh.shape[0]
|
||||||
|
Cb.fill_diagonal_(0.0)
|
||||||
|
bhat = gamma_ewma * bhat + (1 - gamma_ewma) * Cb
|
||||||
|
Ehf = model(ch["yI"], ch["yQ"])
|
||||||
|
Eho = model(ch["yI"], ch["yQ"], topk_mask=oracle)
|
||||||
|
traj["online_cos"].append(mean_cos(Eh, E))
|
||||||
|
traj["full_cos"].append(mean_cos(Ehf, E))
|
||||||
|
traj["oracle_cos"].append(mean_cos(Eho, E))
|
||||||
|
traj["beta_err"].append(float((bhat - Btrue).abs().mean()))
|
||||||
|
print(f"[E9] frame {t}: online={traj['online_cos'][-1]:.4f} "
|
||||||
|
f"oracle={traj['oracle_cos'][-1]:.4f} "
|
||||||
|
f"full={traj['full_cos'][-1]:.4f}", flush=True)
|
||||||
|
|
||||||
|
# ---- wall-clock overhead incl. estimation + argpartition selection
|
||||||
|
timing = {"U": [8, 16, 32, 64, 128], "full_ms": [], "topk_ms": [],
|
||||||
|
"select_ms": []}
|
||||||
|
for Ut in timing["U"]:
|
||||||
|
dt = max(d, 2 * Ut) # keep at least 2 dims per user slot
|
||||||
|
mt = UWCA(dt, Ut, H).to(DEVICE).eval()
|
||||||
|
ch = {"yI": torch.randn(256, dt, device=DEVICE),
|
||||||
|
"yQ": torch.randn(256, dt, device=DEVICE)}
|
||||||
|
bh = torch.rand(Ut, Ut, device=DEVICE)
|
||||||
|
with torch.no_grad():
|
||||||
|
for _ in range(3):
|
||||||
|
mt(ch["yI"], ch["yQ"]) # warm-up
|
||||||
|
torch.cuda.synchronize()
|
||||||
|
t0 = time.perf_counter()
|
||||||
|
for _ in range(20):
|
||||||
|
mt(ch["yI"], ch["yQ"])
|
||||||
|
torch.cuda.synchronize()
|
||||||
|
t_full = (time.perf_counter() - t0) / 20 * 1e3
|
||||||
|
t0 = time.perf_counter()
|
||||||
|
for _ in range(20):
|
||||||
|
b = bh.clone(); b.fill_diagonal_(2.0)
|
||||||
|
idx = torch.topk(b, k, dim=1).indices
|
||||||
|
tkm = torch.zeros(Ut, Ut, dtype=torch.bool, device=DEVICE)
|
||||||
|
tkm.scatter_(1, idx, True)
|
||||||
|
mt(ch["yI"], ch["yQ"], topk_mask=tkm)
|
||||||
|
torch.cuda.synchronize()
|
||||||
|
t_topk = (time.perf_counter() - t0) / 20 * 1e3
|
||||||
|
t0 = time.perf_counter()
|
||||||
|
for _ in range(100):
|
||||||
|
b = bh.clone(); b.fill_diagonal_(2.0)
|
||||||
|
idx = torch.topk(b, k, dim=1).indices
|
||||||
|
torch.cuda.synchronize()
|
||||||
|
t_sel = (time.perf_counter() - t0) / 100 * 1e3
|
||||||
|
timing["full_ms"].append(t_full)
|
||||||
|
timing["topk_ms"].append(t_topk)
|
||||||
|
timing["select_ms"].append(t_sel)
|
||||||
|
print(f"[E9] U={Ut}: full={t_full:.2f}ms topk={t_topk:.2f}ms "
|
||||||
|
f"select={t_sel:.3f}ms", flush=True)
|
||||||
|
|
||||||
|
save_json("e9_topk_online.json", {"trajectory": traj, "timing": timing,
|
||||||
|
"k": k, "warm_frames": warm})
|
||||||
Executable
+490
@@ -0,0 +1,490 @@
|
|||||||
|
"""
|
||||||
|
Shared library for TWC revision-2 experiments (new submission).
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
import copy
|
||||||
|
import json
|
||||||
|
import math
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
import torch.nn as nn
|
||||||
|
import torch.nn.functional as F
|
||||||
|
|
||||||
|
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
||||||
|
DATA = Path(__file__).resolve().parent / "data"
|
||||||
|
DATA.mkdir(exist_ok=True)
|
||||||
|
|
||||||
|
SCENARIOS = {
|
||||||
|
"HIGH": {"beta_u": [0.65, 0.65, 0.60, 0.60], "scenes": [0, 0, 0, 0]},
|
||||||
|
"LOW": {"beta_u": [0.65, 0.05, 0.05, 0.05], "scenes": [0, 1, 2, 3]},
|
||||||
|
"MIX": {"beta_u": [0.65, 0.65, 0.05, 0.05], "scenes": [0, 0, 1, 2]},
|
||||||
|
# 8-slot high-correlation scenario for dynamic-user experiment
|
||||||
|
"HIGH8": {"beta_u": [0.60] * 8, "scenes": [0] * 8},
|
||||||
|
}
|
||||||
|
|
||||||
|
SNR_GRID = np.arange(0.0, 20.0 + 1e-6, 2.0)
|
||||||
|
|
||||||
|
|
||||||
|
def beta_matrix(scen):
|
||||||
|
"""True relevance matrix beta_{u,v} = beta_u beta_v [same scene], else 0."""
|
||||||
|
b = np.asarray(scen["beta_u"], dtype=np.float64)
|
||||||
|
sc = np.asarray(scen["scenes"])
|
||||||
|
U = len(b)
|
||||||
|
B = np.zeros((U, U))
|
||||||
|
for u in range(U):
|
||||||
|
for v in range(U):
|
||||||
|
B[u, v] = 1.0 if u == v else (b[u] * b[v] if sc[u] == sc[v] else 0.0)
|
||||||
|
return B
|
||||||
|
|
||||||
|
|
||||||
|
def gen_embeddings(n, d, U, rng, scen):
|
||||||
|
"""Unit-norm ground-truth embeddings (n, U, d): e_u = sqrt(1-b^2) p + b s."""
|
||||||
|
blend, scenes = scen["beta_u"], scen["scenes"]
|
||||||
|
svecs = {}
|
||||||
|
for sc in sorted(set(scenes)):
|
||||||
|
v = rng.standard_normal(d)
|
||||||
|
svecs[sc] = v / (np.linalg.norm(v) + 1e-8)
|
||||||
|
embs = []
|
||||||
|
for u in range(U):
|
||||||
|
b = blend[u]
|
||||||
|
p = rng.standard_normal((n, d))
|
||||||
|
p /= np.linalg.norm(p, axis=-1, keepdims=True) + 1e-8
|
||||||
|
e = np.sqrt(max(1 - b * b, 0.0)) * p + b * svecs[scenes[u]][None, :]
|
||||||
|
e /= np.linalg.norm(e, axis=-1, keepdims=True) + 1e-8
|
||||||
|
embs.append(e)
|
||||||
|
return torch.from_numpy(np.stack(embs, 1)).float()
|
||||||
|
|
||||||
|
|
||||||
|
class ViewNets(nn.Module):
|
||||||
|
"""Fixed random per-user nonlinear view functions g_u([kappa*s; p_u])."""
|
||||||
|
|
||||||
|
def __init__(self, d, U, seed=7):
|
||||||
|
super().__init__()
|
||||||
|
g = torch.Generator().manual_seed(seed)
|
||||||
|
self.nets = nn.ModuleList()
|
||||||
|
for _ in range(U):
|
||||||
|
l1 = nn.Linear(2 * d, 2 * d)
|
||||||
|
l2 = nn.Linear(2 * d, d)
|
||||||
|
for l in (l1, l2):
|
||||||
|
nn.init.normal_(l.weight, std=(2.0 / l.in_features) ** 0.5, generator=g)
|
||||||
|
nn.init.zeros_(l.bias)
|
||||||
|
self.nets.append(nn.Sequential(l1, nn.Tanh(), l2))
|
||||||
|
for p in self.parameters():
|
||||||
|
p.requires_grad_(False)
|
||||||
|
|
||||||
|
@torch.no_grad()
|
||||||
|
def gen(self, n, d, U, rng, kappa, shared_scene=True):
|
||||||
|
if shared_scene:
|
||||||
|
s = rng.standard_normal((n, d)) / math.sqrt(d)
|
||||||
|
s = np.repeat(s[:, None, :], U, axis=1)
|
||||||
|
else:
|
||||||
|
s = rng.standard_normal((n, U, d)) / math.sqrt(d)
|
||||||
|
p = rng.standard_normal((n, U, d)) / math.sqrt(d)
|
||||||
|
s = torch.from_numpy(s).float().to(DEVICE)
|
||||||
|
p = torch.from_numpy(p).float().to(DEVICE)
|
||||||
|
outs = []
|
||||||
|
for u in range(U):
|
||||||
|
x = torch.cat([kappa * s[:, u], p[:, u]], -1)
|
||||||
|
outs.append(F.normalize(self.nets[u](x), dim=-1))
|
||||||
|
return torch.stack(outs, 1) # (n, U, d)
|
||||||
|
|
||||||
|
|
||||||
|
def block_masks(U, d, device=DEVICE):
|
||||||
|
dpu = d // U
|
||||||
|
m = torch.zeros(U, d, device=device)
|
||||||
|
for u in range(U):
|
||||||
|
m[u, u * dpu:(u + 1) * dpu] = 1.0
|
||||||
|
return m
|
||||||
|
|
||||||
|
|
||||||
|
def channel(E, snr_db, *, phase_sigma_deg=0.0, fading="rayleigh", rician_K_dB=None,
|
||||||
|
nakagami_m=None, offsets=None, h_err_sigma=0.0, masks=None, use_masks=True):
|
||||||
|
"""Single-signal uplink. Returns dict with yI, yQ, h (true magnitude), h_hat.
|
||||||
|
|
||||||
|
E : (n, U, d) ground-truth embeddings on DEVICE.
|
||||||
|
offsets : (n, U) integer per-user timing offsets (symbols), or None.
|
||||||
|
Noise convention: per-rail noise std = sqrt(mean|y_tx|^2 / snr_lin); with
|
||||||
|
phase_sigma = 0 the model reduces exactly to the real-valued model.
|
||||||
|
"""
|
||||||
|
n, U, d = E.shape
|
||||||
|
if masks is None:
|
||||||
|
masks = block_masks(U, d, E.device)
|
||||||
|
X = E * masks[None] if use_masks else E.clone()
|
||||||
|
|
||||||
|
if offsets is not None:
|
||||||
|
Xs = torch.zeros_like(X)
|
||||||
|
offs = offsets
|
||||||
|
for u in range(U):
|
||||||
|
for o in offs[:, u].unique():
|
||||||
|
o = int(o.item())
|
||||||
|
idx = offs[:, u] == o
|
||||||
|
if o == 0:
|
||||||
|
Xs[idx, u] = X[idx, u]
|
||||||
|
else:
|
||||||
|
Xs[idx, u, o:] = X[idx, u, :d - o]
|
||||||
|
X = Xs
|
||||||
|
|
||||||
|
if fading == "rayleigh":
|
||||||
|
hI = torch.randn(n, U, device=E.device) * (0.5 ** 0.5)
|
||||||
|
hQ = torch.randn(n, U, device=E.device) * (0.5 ** 0.5)
|
||||||
|
hmag = (hI ** 2 + hQ ** 2).sqrt()
|
||||||
|
elif fading == "rician":
|
||||||
|
K = 10 ** (rician_K_dB / 10.0)
|
||||||
|
mu = math.sqrt(K / (K + 1))
|
||||||
|
sig = math.sqrt(1.0 / (2 * (K + 1)))
|
||||||
|
hI = mu + torch.randn(n, U, device=E.device) * sig
|
||||||
|
hQ = torch.randn(n, U, device=E.device) * sig
|
||||||
|
hmag = (hI ** 2 + hQ ** 2).sqrt()
|
||||||
|
elif fading == "nakagami":
|
||||||
|
m = nakagami_m
|
||||||
|
gam = torch.distributions.Gamma(m, m).sample((n, U)).to(E.device)
|
||||||
|
hmag = gam.sqrt()
|
||||||
|
else:
|
||||||
|
raise ValueError(fading)
|
||||||
|
|
||||||
|
dphi = torch.randn(n, U, device=E.device) * math.radians(phase_sigma_deg)
|
||||||
|
gI = hmag * torch.cos(dphi)
|
||||||
|
gQ = hmag * torch.sin(dphi)
|
||||||
|
|
||||||
|
yI = (gI[:, :, None] * X).sum(1)
|
||||||
|
yQ = (gQ[:, :, None] * X).sum(1)
|
||||||
|
P = (yI ** 2 + yQ ** 2).mean()
|
||||||
|
nstd = (P / (10 ** (snr_db / 10.0))).sqrt()
|
||||||
|
yI = yI + torch.randn_like(yI) * nstd
|
||||||
|
yQ = yQ + torch.randn_like(yQ) * nstd
|
||||||
|
h_hat = hmag * (1 + torch.randn_like(hmag) * h_err_sigma) if h_err_sigma > 0 else hmag
|
||||||
|
return {"yI": yI, "yQ": yQ, "h": hmag, "h_hat": h_hat, "nvar": float(nstd ** 2)}
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- decoders --
|
||||||
|
class UWCA(nn.Module):
|
||||||
|
"""User-wise cross-attention decoder on the single superimposed signal.
|
||||||
|
|
||||||
|
iq=True : keys/values read the stacked [I; Q] rails (2d input).
|
||||||
|
forward(yI, yQ, active) with active (n, U) bool or None.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, d, U, H=4, iq=False):
|
||||||
|
super().__init__()
|
||||||
|
assert d % H == 0
|
||||||
|
self.d, self.U, self.H, self.dk = d, U, H, d // H
|
||||||
|
self.iq = iq
|
||||||
|
din = 2 * d if iq else d
|
||||||
|
self.q_vectors = nn.Parameter(torch.randn(U, d) * d ** -0.5)
|
||||||
|
self.eta = nn.Parameter(torch.ones(1))
|
||||||
|
self.W_K = nn.Linear(din, d, bias=False)
|
||||||
|
self.W_V = nn.Linear(din, d, bias=False)
|
||||||
|
self.W_O = nn.Linear(d, d, bias=False)
|
||||||
|
self.norm = nn.LayerNorm(d)
|
||||||
|
init_logits = torch.full((U, d), -3.0)
|
||||||
|
dpu = d // U
|
||||||
|
for u in range(U):
|
||||||
|
init_logits[u, u * dpu:(u + 1) * dpu] = 3.0
|
||||||
|
self.mask_logits = nn.Parameter(init_logits)
|
||||||
|
|
||||||
|
def soft_masks(self):
|
||||||
|
return torch.sigmoid(self.mask_logits)
|
||||||
|
|
||||||
|
def forward(self, yI, yQ=None, active=None, topk_mask=None, return_alpha=False):
|
||||||
|
# yI: (n, d) shared signal, or (n, U, d) per-candidate aligned copies
|
||||||
|
U, H, dk = self.U, self.H, self.dk
|
||||||
|
m = self.soft_masks() # (U, d)
|
||||||
|
if yI.dim() == 2:
|
||||||
|
n, d = yI.shape
|
||||||
|
yIc = yI[:, None, :].expand(-1, U, -1)
|
||||||
|
else:
|
||||||
|
n, _, d = yI.shape
|
||||||
|
yIc = yI
|
||||||
|
R = yIc * m[None] # (n, U, d)
|
||||||
|
if self.iq:
|
||||||
|
yQc = yQ[:, None, :].expand(-1, U, -1) if yQ.dim() == 2 else yQ
|
||||||
|
RQ = yQc * m[None]
|
||||||
|
Rin = torch.cat([R, RQ], -1) # (n, U, 2d)
|
||||||
|
else:
|
||||||
|
Rin = R
|
||||||
|
K = self.W_K(Rin).view(n, U, H, dk) # (n, Uk, H, dk)
|
||||||
|
V = self.W_V(Rin).view(n, U, H, dk)
|
||||||
|
Q = self.q_vectors.view(U, H, dk) # (Uq, H, dk)
|
||||||
|
scores = torch.einsum("qhk,nihk->nqhi", Q, K) * self.eta / dk ** 0.5
|
||||||
|
if active is not None: # (n, U) bool
|
||||||
|
scores = scores.masked_fill(~active[:, None, None, :], -1e9)
|
||||||
|
if topk_mask is not None: # (U, U) bool keep
|
||||||
|
scores = scores.masked_fill(~topk_mask[None, :, None, :], -1e9)
|
||||||
|
alpha = F.softmax(scores, dim=-1) # (n, Uq, H, Uk)
|
||||||
|
ctx = torch.einsum("nqhi,nihk->nqhk", alpha, V).reshape(n, U, d)
|
||||||
|
own = yIc * m[None]
|
||||||
|
out = F.normalize(self.norm(self.W_O(ctx) + own), dim=-1)
|
||||||
|
if return_alpha:
|
||||||
|
return out, alpha.mean(dim=(0, 2))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
class Encoder(nn.Module):
|
||||||
|
"""Trainable semantic encoder for the end-to-end experiment."""
|
||||||
|
|
||||||
|
def __init__(self, d):
|
||||||
|
super().__init__()
|
||||||
|
self.net = nn.Sequential(nn.Linear(d, 2 * d), nn.LayerNorm(2 * d),
|
||||||
|
nn.GELU(), nn.Linear(2 * d, d))
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
return F.normalize(self.net(x), dim=-1)
|
||||||
|
|
||||||
|
|
||||||
|
def ofdma_decode(yI, masks):
|
||||||
|
return F.normalize(yI[:, None, :] * masks[None], dim=-1)
|
||||||
|
|
||||||
|
|
||||||
|
def tdma_proj_decode(E, snr_db, rng_t):
|
||||||
|
"""Orthogonal scheme with an arbitrary (random orthonormal) d/U-dim
|
||||||
|
projection per user instead of coordinate masks: z_u = h_u P_u e_u + n."""
|
||||||
|
n, U, d = E.shape
|
||||||
|
dpu = d // U
|
||||||
|
Q, _ = torch.linalg.qr(torch.randn(d, d, generator=rng_t).to(E.device))
|
||||||
|
outs = []
|
||||||
|
hmag = (torch.randn(n, U, device=E.device) ** 2 +
|
||||||
|
torch.randn(n, U, device=E.device) ** 2).sqrt() * 0.5 ** 0.5
|
||||||
|
snr_lin = 10 ** (snr_db / 10.0)
|
||||||
|
for u in range(U):
|
||||||
|
P = Q[u * dpu:(u + 1) * dpu] # (dpu, d)
|
||||||
|
z = hmag[:, u:u + 1] * (E[:, u] @ P.T) # (n, dpu)
|
||||||
|
nstd = (z.pow(2).mean() / snr_lin).sqrt()
|
||||||
|
z = z + torch.randn_like(z) * nstd
|
||||||
|
outs.append(F.normalize(z @ P, dim=-1))
|
||||||
|
return torch.stack(outs, 1)
|
||||||
|
|
||||||
|
|
||||||
|
def lmmse_decode(yI, h, nvar, B, masks, genie=True):
|
||||||
|
"""Closed-form linear MMSE on the block model. C_vv = I/d, C_uv = B_uv I/d.
|
||||||
|
Block-diagonal C_y => per-block Wiener weights w_uv = h_v B_uv/d / (h_v^2/d + nvar).
|
||||||
|
genie=False zeroes the cross terms (correlation-blind)."""
|
||||||
|
n, d = yI.shape
|
||||||
|
U = masks.shape[0]
|
||||||
|
Bm = torch.as_tensor(B, dtype=torch.float32, device=yI.device)
|
||||||
|
if not genie:
|
||||||
|
Bm = torch.eye(U, device=yI.device)
|
||||||
|
yb = yI[:, None, :] * masks[None] # (n, Uv, d) block pieces
|
||||||
|
w = (h[:, None, :] * Bm[None] / d) / (h[:, None, :] ** 2 / d + nvar) # (n,Uu,Uv)
|
||||||
|
est = torch.einsum("nuv,nvd->nud", w, yb)
|
||||||
|
return F.normalize(est, dim=-1)
|
||||||
|
|
||||||
|
|
||||||
|
def noma_sic_decode(E, snr_db):
|
||||||
|
"""Full-band power-domain NOMA with SIC (no masks)."""
|
||||||
|
n, U, d = E.shape
|
||||||
|
pa = torch.tensor([0.40, 0.30, 0.20, 0.10], device=E.device)[:U]
|
||||||
|
pa = pa / pa.sum()
|
||||||
|
h = (torch.randn(n, U, 1, device=E.device) ** 2 +
|
||||||
|
torch.randn(n, U, 1, device=E.device) ** 2).sqrt() * 0.5 ** 0.5
|
||||||
|
y = (E * pa.sqrt()[None, :, None] * h).sum(1)
|
||||||
|
nstd = (y.pow(2).mean() / 10 ** (snr_db / 10.0)).sqrt()
|
||||||
|
y = y + torch.randn(n, d, device=E.device) * nstd
|
||||||
|
order = torch.argsort(pa, descending=True)
|
||||||
|
res = y.clone()
|
||||||
|
out = torch.zeros_like(E)
|
||||||
|
for ui in order:
|
||||||
|
u = int(ui.item())
|
||||||
|
eh = F.normalize(res / (h[:, u] + 1e-8), dim=-1)
|
||||||
|
out[:, u] = eh
|
||||||
|
res = res - h[:, u] * pa[u].sqrt() * eh
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------ losses --
|
||||||
|
def semantic_loss(Ehat, E, lam=0.1, active=None):
|
||||||
|
cos = (Ehat * E).sum(-1)
|
||||||
|
if active is not None:
|
||||||
|
distortion = ((1 - cos) * active).sum() / active.sum()
|
||||||
|
else:
|
||||||
|
distortion = (1 - cos).mean()
|
||||||
|
U = Ehat.shape[1]
|
||||||
|
emb = Ehat.mean(0)
|
||||||
|
ec = emb - emb.mean(1, keepdim=True)
|
||||||
|
en = F.normalize(ec, dim=1)
|
||||||
|
C = en @ en.T
|
||||||
|
off = C[~torch.eye(U, dtype=torch.bool, device=Ehat.device)].abs().mean()
|
||||||
|
return distortion + lam * off
|
||||||
|
|
||||||
|
|
||||||
|
def ser(Ehat, E, tau=0.45, active=None):
|
||||||
|
bad = ((Ehat * E).sum(-1) < tau).float()
|
||||||
|
if active is not None:
|
||||||
|
return float((bad * active).sum() / active.sum())
|
||||||
|
return float(bad.mean())
|
||||||
|
|
||||||
|
|
||||||
|
def mean_cos(Ehat, E, active=None):
|
||||||
|
c = (Ehat * E).sum(-1)
|
||||||
|
if active is not None:
|
||||||
|
return float((c * active).sum() / active.sum())
|
||||||
|
return float(c.mean())
|
||||||
|
|
||||||
|
|
||||||
|
def sample_corr(A, Bt):
|
||||||
|
"""Mean per-sample Pearson correlation across the d dims of two (n,d) tensors."""
|
||||||
|
Ac = A - A.mean(-1, keepdim=True)
|
||||||
|
Bc = Bt - Bt.mean(-1, keepdim=True)
|
||||||
|
num = (Ac * Bc).sum(-1)
|
||||||
|
den = Ac.norm(dim=-1) * Bc.norm(dim=-1) + 1e-9
|
||||||
|
return float((num / den).mean())
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------- training --
|
||||||
|
def train_multitask(model, gen_fn, tasks, epochs=300, batch=64, lam=0.1,
|
||||||
|
outer_lr=1e-3, mask_lr_mult=100.0, log_every=50, tag="",
|
||||||
|
encoder=None, extra_loss=None, log_state=None):
|
||||||
|
"""Multi-SNR/-condition aggregated training (the manuscript's outer objective
|
||||||
|
without inner adaptation). gen_fn(batch) -> E ground truth on DEVICE.
|
||||||
|
tasks: list of dicts of channel kwargs incl. 'snr_db'."""
|
||||||
|
params = []
|
||||||
|
mask_p = [p for nm, p in model.named_parameters() if "mask_logits" in nm]
|
||||||
|
other = [p for nm, p in model.named_parameters() if "mask_logits" not in nm]
|
||||||
|
params = [{"params": other, "lr": outer_lr},
|
||||||
|
{"params": mask_p, "lr": outer_lr * mask_lr_mult}]
|
||||||
|
if encoder is not None:
|
||||||
|
params.append({"params": encoder.parameters(), "lr": outer_lr})
|
||||||
|
opt = torch.optim.Adam(params)
|
||||||
|
hist = []
|
||||||
|
for ep in range(1, epochs + 1):
|
||||||
|
loss_acc = 0.0
|
||||||
|
opt.zero_grad()
|
||||||
|
for t in tasks:
|
||||||
|
E = gen_fn(batch)
|
||||||
|
if encoder is not None:
|
||||||
|
n, U, d = E.shape
|
||||||
|
E = encoder(E.reshape(-1, d)).reshape(n, U, d)
|
||||||
|
ch = channel(E, **t)
|
||||||
|
Eh = model(ch["yI"], ch["yQ"])
|
||||||
|
L = semantic_loss(Eh, E, lam)
|
||||||
|
if extra_loss is not None:
|
||||||
|
L = L + extra_loss(E)
|
||||||
|
loss_acc += L
|
||||||
|
(loss_acc / len(tasks)).backward()
|
||||||
|
nn.utils.clip_grad_norm_(model.parameters(), 5.0)
|
||||||
|
opt.step()
|
||||||
|
hist.append(float(loss_acc) / len(tasks))
|
||||||
|
if log_state is not None:
|
||||||
|
gn = sum(float(p.grad.norm()) ** 2 for p in model.parameters()
|
||||||
|
if p.grad is not None) ** 0.5
|
||||||
|
log_state.append({"ep": ep, "eta": float(model.eta), "gnorm": gn})
|
||||||
|
if ep % log_every == 0:
|
||||||
|
print(f" [{tag}] ep {ep}/{epochs} loss={hist[-1]:.4f}", flush=True)
|
||||||
|
return hist
|
||||||
|
|
||||||
|
|
||||||
|
def fomaml_train(model, gen_fn, tasks, epochs=200, batch=64, lam=0.1,
|
||||||
|
inner_lr=0.01, inner_steps=5, outer_lr=1e-3,
|
||||||
|
tasks_per_step=8, log_every=25, tag="fomaml", log_state=None,
|
||||||
|
rng=None):
|
||||||
|
"""Proper first-order MAML: inner SGD on support, outer update from query
|
||||||
|
gradients evaluated at the adapted parameters."""
|
||||||
|
opt = torch.optim.Adam(model.parameters(), lr=outer_lr)
|
||||||
|
rng = rng or np.random.default_rng(0)
|
||||||
|
hist = []
|
||||||
|
names = [nm for nm, _ in model.named_parameters()]
|
||||||
|
for ep in range(1, epochs + 1):
|
||||||
|
idx = rng.choice(len(tasks), size=min(tasks_per_step, len(tasks)),
|
||||||
|
replace=False)
|
||||||
|
grads = {nm: torch.zeros_like(p) for nm, p in model.named_parameters()}
|
||||||
|
qloss_acc = 0.0
|
||||||
|
for ti in idx:
|
||||||
|
t = tasks[ti]
|
||||||
|
adapted = copy.deepcopy(model)
|
||||||
|
iopt = torch.optim.SGD(adapted.parameters(), lr=inner_lr)
|
||||||
|
for _ in range(inner_steps):
|
||||||
|
E = gen_fn(batch)
|
||||||
|
ch = channel(E, **t)
|
||||||
|
L = semantic_loss(adapted(ch["yI"], ch["yQ"]), E, lam)
|
||||||
|
iopt.zero_grad(); L.backward(); iopt.step()
|
||||||
|
E = gen_fn(batch)
|
||||||
|
ch = channel(E, **t)
|
||||||
|
qL = semantic_loss(adapted(ch["yI"], ch["yQ"]), E, lam)
|
||||||
|
adapted.zero_grad(); qL.backward()
|
||||||
|
for nm, p in adapted.named_parameters():
|
||||||
|
if p.grad is not None:
|
||||||
|
grads[nm] += p.grad
|
||||||
|
qloss_acc += float(qL)
|
||||||
|
opt.zero_grad()
|
||||||
|
for nm, p in model.named_parameters():
|
||||||
|
p.grad = grads[nm] / len(idx)
|
||||||
|
nn.utils.clip_grad_norm_(model.parameters(), 5.0)
|
||||||
|
opt.step()
|
||||||
|
hist.append(qloss_acc / len(idx))
|
||||||
|
if log_state is not None:
|
||||||
|
gn = sum(float(g.norm()) ** 2 for g in grads.values()) ** 0.5 / len(idx)
|
||||||
|
log_state.append({"ep": ep, "eta": float(model.eta), "gnorm": gn})
|
||||||
|
if ep % log_every == 0:
|
||||||
|
print(f" [{tag}] ep {ep}/{epochs} qloss={hist[-1]:.4f} "
|
||||||
|
f"eta={float(model.eta):.3f}", flush=True)
|
||||||
|
return hist
|
||||||
|
|
||||||
|
|
||||||
|
def adapt(model, gen_fn, task, steps=5, inner_lr=0.01, batch=64, lam=0.1):
|
||||||
|
adapted = copy.deepcopy(model)
|
||||||
|
iopt = torch.optim.SGD(adapted.parameters(), lr=inner_lr)
|
||||||
|
for _ in range(steps):
|
||||||
|
E = gen_fn(batch)
|
||||||
|
ch = channel(E, **task)
|
||||||
|
L = semantic_loss(adapted(ch["yI"], ch["yQ"]), E, lam)
|
||||||
|
iopt.zero_grad(); L.backward(); iopt.step()
|
||||||
|
return adapted
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------- evaluation --
|
||||||
|
@torch.no_grad()
|
||||||
|
def eval_scheme(scheme, gen_fn, task, n_mc=200, batch=64, tau=0.45, model=None,
|
||||||
|
B=None, masks=None, encoder=None, rng_t=None, active_fn=None,
|
||||||
|
topk_mask=None):
|
||||||
|
"""Returns (ser, cos) for one task/channel config."""
|
||||||
|
s_acc = c_acc = 0.0
|
||||||
|
for _ in range(n_mc):
|
||||||
|
E = gen_fn(batch)
|
||||||
|
if encoder is not None:
|
||||||
|
n, U, d = E.shape
|
||||||
|
E = encoder(E.reshape(-1, d)).reshape(n, U, d)
|
||||||
|
act = active_fn(E.shape[0]) if active_fn is not None else None
|
||||||
|
if scheme == "uwca":
|
||||||
|
ch = channel(E, **task)
|
||||||
|
Eh = model(ch["yI"], ch["yQ"], active=act, topk_mask=topk_mask)
|
||||||
|
elif scheme == "ofdma":
|
||||||
|
ch = channel(E, **task)
|
||||||
|
m = masks if masks is not None else block_masks(E.shape[1], E.shape[2],
|
||||||
|
E.device)
|
||||||
|
Eh = ofdma_decode(ch["yI"], m)
|
||||||
|
elif scheme == "sfdma":
|
||||||
|
ch = channel(E, **task)
|
||||||
|
m = masks
|
||||||
|
Eh = torch.stack([F.normalize((ch["yI"] * m[u]) /
|
||||||
|
(ch["h_hat"][:, u:u + 1] + 1e-8), dim=-1)
|
||||||
|
for u in range(m.shape[0])], 1)
|
||||||
|
elif scheme == "noma":
|
||||||
|
Eh = noma_sic_decode(E, task["snr_db"])
|
||||||
|
elif scheme in ("lmmse_genie", "lmmse_blind"):
|
||||||
|
ch = channel(E, **task)
|
||||||
|
Eh = lmmse_decode(ch["yI"], ch["h_hat"], ch["nvar"], B, masks,
|
||||||
|
genie=(scheme == "lmmse_genie"))
|
||||||
|
elif scheme == "tdma_proj":
|
||||||
|
Eh = tdma_proj_decode(E, task["snr_db"], rng_t)
|
||||||
|
else:
|
||||||
|
raise ValueError(scheme)
|
||||||
|
s_acc += ser(Eh, E, tau, act)
|
||||||
|
c_acc += mean_cos(Eh, E, act)
|
||||||
|
return s_acc / n_mc, c_acc / n_mc
|
||||||
|
|
||||||
|
|
||||||
|
def save_json(name, obj):
|
||||||
|
p = DATA / name
|
||||||
|
with open(p, "w") as f:
|
||||||
|
json.dump(obj, f, indent=1)
|
||||||
|
print(f"saved -> {p}", flush=True)
|
||||||
|
|
||||||
|
|
||||||
|
def set_seed(seed=42):
|
||||||
|
torch.manual_seed(seed)
|
||||||
|
np.random.seed(seed)
|
||||||
|
return np.random.default_rng(seed)
|
||||||
Executable
+153
@@ -0,0 +1,153 @@
|
|||||||
|
"""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 --
|
||||||
|
d = json.load(open(DATA / "e1_fair_baselines.json"))
|
||||||
|
snr = d["snr"]
|
||||||
|
f = plt.figure(figsize=(7.1, 2.75))
|
||||||
|
axs = [f.add_axes([0.115, 0.165, 0.365, 0.77]),
|
||||||
|
f.add_axes([0.615, 0.165, 0.365, 0.77])]
|
||||||
|
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_title(ttl)
|
||||||
|
ax.set_xlim(0, 20)
|
||||||
|
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")
|
||||||
Reference in New Issue
Block a user