Add the diagnostics behind the no-interference claim
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Where does the legitimate advantage over OMA go?
|
||||
|
||||
An ideal M-ary receiver at the main configuration should reach 0.199 at
|
||||
10 dB against the 0.275 of resource-matched OMA, a factor of 1.38, while
|
||||
the system measures 0.257, a factor of 1.07. This script splits the
|
||||
shortfall into its two causes: residual multi-user interference, which
|
||||
orthogonal keys do not remove because masking is elementwise, and the
|
||||
distance the trained unit codebook falls short of an orthogonal set.
|
||||
"""
|
||||
import math
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import torch
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
import sse_lib as L
|
||||
from sse_lib import DEVICE, snr_to_sigma2, rayleigh_gain
|
||||
from exp_full import main_model
|
||||
|
||||
SNR_DB = 10.0
|
||||
FRAMES = 400_000
|
||||
CH = 40_000
|
||||
|
||||
|
||||
def ser(m, snr_db, frames, solo=False):
|
||||
"""SER of user 0. With solo=True the other users transmit nothing,
|
||||
while the power normalizer c is left at its four-user value so that
|
||||
user 0 keeps exactly the energy it has in the real system."""
|
||||
tot = wrong = 0
|
||||
with torch.no_grad():
|
||||
while tot < frames:
|
||||
n = min(CH, frames - tot)
|
||||
dig = torch.randint(m.vu, (n, m.users, m.P), device=DEVICE)
|
||||
Bn = m.unit_codebook()
|
||||
e = Bn[dig] / math.sqrt(m.P)
|
||||
mk = m.masks()
|
||||
x = e * mk[None, :, None, :]
|
||||
if solo:
|
||||
x = x[:, :1]
|
||||
y = x.sum(dim=1) / m.c
|
||||
h = rayleigh_gain((n, 1), device=DEVICE)
|
||||
sig = snr_to_sigma2(torch.full((n,), snr_db), m.d).to(DEVICE).sqrt()
|
||||
rx = h[:, :, None, None] * y[:, None] \
|
||||
+ sig[:, None, None, None] * torch.randn(n, 1, m.P, m.L,
|
||||
device=DEVICE)
|
||||
r = rx / h[:, :, None, None].clamp_min(1e-6)
|
||||
cand = Bn[None, :, :] * mk[:1, None, :]
|
||||
sc = torch.einsum("nupl,uvl->nupv", r, cand)
|
||||
bad = (sc.argmax(-1)[:, 0] != dig[:, 0]).any(dim=-1)
|
||||
wrong += int(bad.sum())
|
||||
tot += n
|
||||
return wrong / tot
|
||||
|
||||
|
||||
def main():
|
||||
m = main_model()
|
||||
with torch.no_grad():
|
||||
Bn = m.unit_codebook()
|
||||
G = Bn @ Bn.T
|
||||
off = G - torch.diag(torch.diag(G))
|
||||
mk = m.masks()
|
||||
Gm = mk @ mk.T / m.L
|
||||
offm = Gm - torch.diag(torch.diag(Gm))
|
||||
|
||||
print("main configuration: d=%d P=%d L=%d Vu=%d U=%d"
|
||||
% (m.d, m.P, m.L, m.vu, m.users))
|
||||
print("key cross-correlation, max |off-diagonal| : %.2e"
|
||||
% offm.abs().max())
|
||||
print("codebook Gram, max |off-diagonal| : %.4f"
|
||||
% off.abs().max())
|
||||
print("codebook Gram, rms off-diagonal : %.4f"
|
||||
% off.pow(2).sum().div(m.vu * (m.vu - 1)).sqrt())
|
||||
print("(an orthogonal set of %d codewords in %d dims would read 0)"
|
||||
% (m.vu, m.L))
|
||||
print()
|
||||
four = ser(m, SNR_DB, FRAMES, solo=False)
|
||||
solo = ser(m, SNR_DB, FRAMES, solo=True)
|
||||
print("user-0 SER, all four users transmitting : %.4f" % four)
|
||||
print("user-0 SER, other users silent : %.4f" % solo)
|
||||
print("OMA, resource matched (closed form) : %.4f"
|
||||
% L.oma_ser([SNR_DB])[0])
|
||||
print("ideal 16-ary orthogonal (separate MC) : 0.1986")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user