""" ============================================================================= 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")