Simulation code and data for the TMC submission
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
"""E3: dynamic mobile environment -- time-varying share coefficients a_u(t)
|
||||
from random-waypoint trajectories, with sparse affinity-pilot tracking.
|
||||
|
||||
Every K-th slot each user sends n_p orthogonal pilot embeddings (overhead
|
||||
n_p/(K*batch) << 1); the tracker EWMA-smooths the triangulated observations.
|
||||
|
||||
Methods:
|
||||
DR-genie : decomposition receiver with true a_u(t) (upper ref)
|
||||
DR-tracked : DR with pilot-tracked a_hat(t) (proposed)
|
||||
DR-static : DR designed for the time-averaged a (no adaptation)
|
||||
SR / SC / LMMSE : baselines with true B(t)
|
||||
|
||||
Outputs: data/e3_timeseries.csv, data/e3_speed.csv
|
||||
(figures come from replot_all.py only)
|
||||
"""
|
||||
import os
|
||||
import numpy as np
|
||||
from semantic_mac import (affinity_matrix, matched_filter, demux_sr, demux_sc,
|
||||
demux_lmmse, demux_dr, sample_latents_isotropic,
|
||||
mobility_trajectories, AffinityTracker,
|
||||
pilot_affinity_obs, metrics,
|
||||
oma_observe, demux_noma_genie)
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
FIG = os.path.join(HERE, "..", "fig")
|
||||
DATA = os.path.join(HERE, "..", "data")
|
||||
os.makedirs(FIG, exist_ok=True)
|
||||
os.makedirs(DATA, exist_ok=True)
|
||||
|
||||
U, D, DC = 4, 64, 16
|
||||
BATCH = 320
|
||||
SNR_DB = 12
|
||||
RHO = 10 ** (SNR_DB / 10)
|
||||
T = 300
|
||||
K_PILOT = 5 # pilot every K slots
|
||||
N_PILOT = 64 # pilot embeddings per user per pilot slot
|
||||
LAM = 0.3
|
||||
METHODS = ["DR-genie", "DR-tracked", "DR-static", "SR", "SC", "LMMSE",
|
||||
"OMA", "NOMA"]
|
||||
MOB = dict(box=50.0, r_scene=32.0, a_max=0.95)
|
||||
|
||||
|
||||
def run_trace(a_t, rng, collect_ts=False):
|
||||
Vc = np.eye(D)[:, :DC]
|
||||
a_bar = a_t.mean(axis=0)
|
||||
tracker = AffinityTracker(U, lam=LAM, a_init=float(a_bar.mean()))
|
||||
sers = {m: [] for m in METHODS}
|
||||
coss = {m: [] for m in METHODS}
|
||||
a_hat_log = []
|
||||
for t in range(a_t.shape[0]):
|
||||
a = a_t[t]
|
||||
B = affinity_matrix(a)
|
||||
z = sample_latents_isotropic(BATCH, U, D, DC, a, rng)
|
||||
tilde, h = matched_filter(z, B, RHO, rng)
|
||||
|
||||
if t % K_PILOT == 0:
|
||||
zp = sample_latents_isotropic(N_PILOT, U, D, DC, a, rng)
|
||||
hp = np.clip(np.abs((rng.standard_normal(U) +
|
||||
1j * rng.standard_normal(U)) / np.sqrt(2)),
|
||||
0.2, None)
|
||||
tracker.update(pilot_affinity_obs(zp, hp, RHO, rng))
|
||||
a_hat = np.clip(tracker.a, 0.02, 0.95)
|
||||
|
||||
out = {}
|
||||
out["DR-genie"] = demux_dr(tilde, B, h, RHO, a, Vc)
|
||||
out["DR-tracked"] = demux_dr(tilde, affinity_matrix(a_hat), h, RHO,
|
||||
a_hat, Vc)
|
||||
out["DR-static"] = demux_dr(tilde, affinity_matrix(a_bar), h, RHO,
|
||||
a_bar, Vc)
|
||||
out["SR"] = demux_sr(tilde, B, h)
|
||||
out["SC"] = demux_sc(tilde, h)
|
||||
out["LMMSE"], _ = demux_lmmse(tilde, B, h, RHO)
|
||||
out["OMA"] = oma_observe(z, h, RHO, rng)
|
||||
out["NOMA"] = demux_noma_genie(z, h, RHO, rng)
|
||||
|
||||
a_hat_log.append(a_hat)
|
||||
for m in METHODS:
|
||||
c, _, s = metrics(out[m], z)
|
||||
sers[m].append(s)
|
||||
coss[m].append(c)
|
||||
res = {m: (float(np.mean(coss[m])), float(np.mean(sers[m])))
|
||||
for m in METHODS}
|
||||
if collect_ts:
|
||||
return res, sers, np.array(a_hat_log)
|
||||
return res
|
||||
|
||||
|
||||
def drive_through_profile(T, peaks=(80, 110, 140, 170), width=45.0,
|
||||
a_lo=0.05, a_hi=0.9):
|
||||
"""Scene pass-by: each user approaches the shared scene, dwells, leaves."""
|
||||
t = np.arange(T)[:, None]
|
||||
pk = np.asarray(peaks)[None, :]
|
||||
return a_lo + (a_hi - a_lo) * np.exp(-(t - pk) ** 2 / (2 * width ** 2))
|
||||
|
||||
|
||||
def main():
|
||||
# --- time-series: scene pass-by, averaged over REPS_TS runs -------------
|
||||
a_t = drive_through_profile(T)
|
||||
REPS_TS = 5
|
||||
sers_acc = None
|
||||
means_acc = {m: [] for m in METHODS}
|
||||
for rep_i in range(REPS_TS):
|
||||
rng = np.random.default_rng(3 + rep_i)
|
||||
res_i, sers_i, a_hat_i = run_trace(a_t, rng, collect_ts=True)
|
||||
if rep_i == 0:
|
||||
a_hat_log = a_hat_i
|
||||
if sers_acc is None:
|
||||
sers_acc = {m: np.array(sers_i[m], float) for m in METHODS}
|
||||
else:
|
||||
for m in METHODS:
|
||||
sers_acc[m] += np.array(sers_i[m], float)
|
||||
for m in METHODS:
|
||||
means_acc[m].append(res_i[m])
|
||||
print(f" ts rep {rep_i+1}/{REPS_TS} done")
|
||||
sers = {m: (sers_acc[m] / REPS_TS).tolist() for m in METHODS}
|
||||
res = {m: (float(np.mean([x[0] for x in means_acc[m]])),
|
||||
float(np.mean([x[1] for x in means_acc[m]])))
|
||||
for m in METHODS}
|
||||
print("time-series means:", {m: f"cos={v[0]:.3f},ser={v[1]:.3f}"
|
||||
for m, v in res.items()})
|
||||
with open(os.path.join(DATA, "e3_timeseries.csv"), "w") as f:
|
||||
f.write("t," + ",".join(f"a{u}" for u in range(U)) + ","
|
||||
+ ",".join(f"ahat{u}" for u in range(U)) + ","
|
||||
+ ",".join(f"{m}_ser" for m in METHODS) + "\n")
|
||||
for t in range(T):
|
||||
f.write(f"{t}," + ",".join(f"{a_t[t,u]:.4f}" for u in range(U))
|
||||
+ "," + ",".join(f"{a_hat_log[t,u]:.4f}" for u in range(U))
|
||||
+ "," + ",".join(f"{sers[m][t]:.4f}" for m in METHODS) + "\n")
|
||||
|
||||
# --- speed sweep (averaged over trajectory seeds) -----------------------
|
||||
speeds = [0.5, 1.0, 2.0, 4.0, 8.0]
|
||||
reps = 8
|
||||
rows = []
|
||||
for si, sp in enumerate(speeds):
|
||||
acc = {m: [] for m in METHODS}
|
||||
for rep in range(reps):
|
||||
# disjoint seed blocks per speed point (no seed reuse across
|
||||
# speeds)
|
||||
rng_s = np.random.default_rng(1000 + 100 * si + rep)
|
||||
a_tr = mobility_trajectories(U, T, sp, rng_s, **MOB)
|
||||
r = run_trace(a_tr, rng_s)
|
||||
for m in METHODS:
|
||||
acc[m].append(r[m])
|
||||
rows.append({m: (float(np.mean([x[0] for x in acc[m]])),
|
||||
float(np.mean([x[1] for x in acc[m]])))
|
||||
for m in METHODS})
|
||||
print(f"speed={sp} " + " ".join(f"{m}:ser={rows[-1][m][1]:.3f}"
|
||||
for m in METHODS))
|
||||
with open(os.path.join(DATA, "e3_speed.csv"), "w") as f:
|
||||
f.write("speed," + ",".join(f"{m}_cos,{m}_ser" for m in METHODS) + "\n")
|
||||
for sp, r in zip(speeds, rows):
|
||||
f.write(f"{sp}," + ",".join(f"{r[m][0]},{r[m][1]}"
|
||||
for m in METHODS) + "\n")
|
||||
|
||||
# figures are produced only by the canonical replot_all.py (uniform
|
||||
# geometry); experiment scripts write CSVs exclusively.
|
||||
print("E3 done. Run replot_all.py to regenerate the figures.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user