data: add SC-LDPC results and comprehensive comparison plots
SC-LDPC threshold saturation results: - SC original staircase: 2.28 photons/slot (vs 4.76 uncoupled) - SC DE-optimized: 1.03 photons/slot (vs 3.21 uncoupled) - Shannon limit: 0.47 photons/slot (remaining gap: 3.4 dB) Add CLI to sc_ldpc.py (threshold, fer-compare, full subcommands). Add threshold progression, SC threshold bars, and SC FER plots. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ from pathlib import Path
|
||||
|
||||
RESULTS_PATH = Path(__file__).parent.parent / 'data' / 'de_results.json'
|
||||
RESULTS_Z128_PATH = Path(__file__).parent.parent / 'data' / 'de_results_z128.json'
|
||||
SC_RESULTS_PATH = Path(__file__).parent.parent / 'data' / 'sc_ldpc_results.json'
|
||||
OUT_DIR = Path(__file__).parent.parent / 'data' / 'plots'
|
||||
|
||||
# Presentation-friendly style
|
||||
@@ -294,6 +295,180 @@ def plot_z128_comparison(data_z32, data_z128):
|
||||
print(f' Saved z128_fer_comparison.png')
|
||||
|
||||
|
||||
def load_sc_results():
|
||||
"""Load SC-LDPC results if available."""
|
||||
if SC_RESULTS_PATH.exists():
|
||||
with open(SC_RESULTS_PATH) as f:
|
||||
return json.load(f)
|
||||
return None
|
||||
|
||||
|
||||
def plot_threshold_progression(data_z32, data_sc):
|
||||
"""
|
||||
Threshold progression: original -> DE-optimized -> normalized -> SC-LDPC,
|
||||
with Shannon limit line. The key summary plot.
|
||||
"""
|
||||
fig, ax = plt.subplots(figsize=(10, 5.5))
|
||||
|
||||
shannon_limit = 0.47
|
||||
|
||||
# Build progression data
|
||||
stages = []
|
||||
values = []
|
||||
colors_bar = []
|
||||
|
||||
# Stage 1: Original staircase (offset)
|
||||
orig_thresh = data_z32['reference_thresholds'].get(
|
||||
'Original staircase [7,2,2,2,2,2,2,1]', 5.23)
|
||||
stages.append('Original\nstaircase\n(offset)')
|
||||
values.append(float(orig_thresh))
|
||||
colors_bar.append('#d62728')
|
||||
|
||||
# Stage 2: DE-optimized (offset)
|
||||
opt_thresh = data_z32['best_threshold']
|
||||
stages.append('DE-optimized\n(offset)')
|
||||
values.append(float(opt_thresh))
|
||||
colors_bar.append('#ff7f0e')
|
||||
|
||||
# Stage 3: Normalized min-sum
|
||||
if data_sc and 'uncoupled_thresholds' in data_sc:
|
||||
norm_thresh = data_sc['uncoupled_thresholds'].get('optimized_normalized', opt_thresh)
|
||||
stages.append('DE-optimized\n(normalized)')
|
||||
values.append(float(norm_thresh))
|
||||
colors_bar.append('#2ca02c')
|
||||
|
||||
# Stage 4: SC-LDPC
|
||||
if data_sc and 'sc_thresholds' in data_sc:
|
||||
sc_thresh = data_sc['sc_thresholds'].get('sc_optimized',
|
||||
data_sc['sc_thresholds'].get('sc_original', None))
|
||||
if sc_thresh is not None:
|
||||
stages.append('SC-LDPC\n(normalized)')
|
||||
values.append(float(sc_thresh))
|
||||
colors_bar.append('#9467bd')
|
||||
|
||||
x = np.arange(len(stages))
|
||||
bars = ax.bar(x, values, color=colors_bar, width=0.6, edgecolor='white', linewidth=1.5)
|
||||
|
||||
# Value labels on bars
|
||||
for bar, val in zip(bars, values):
|
||||
ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.08,
|
||||
f'{val:.2f}', ha='center', va='bottom', fontweight='bold', fontsize=13)
|
||||
|
||||
# Shannon limit line
|
||||
ax.axhline(y=shannon_limit, color='black', linestyle='--', linewidth=2, alpha=0.7)
|
||||
ax.text(len(stages) - 0.5, shannon_limit + 0.08,
|
||||
f'Shannon limit ({shannon_limit})',
|
||||
fontsize=11, ha='right', va='bottom', style='italic', alpha=0.7)
|
||||
|
||||
# dB annotations between bars
|
||||
for i in range(1, len(values)):
|
||||
if values[i] > 0 and values[i-1] > 0:
|
||||
gain_db = 10 * np.log10(values[i-1] / values[i])
|
||||
mid_y = (values[i-1] + values[i]) / 2
|
||||
ax.annotate(f'{gain_db:.1f} dB',
|
||||
xy=(i, values[i] + 0.02),
|
||||
xytext=(i - 0.5, mid_y + 0.3),
|
||||
fontsize=10, color='#555555',
|
||||
arrowprops=dict(arrowstyle='->', color='#999999', lw=1),
|
||||
ha='center')
|
||||
|
||||
# Total gap annotation
|
||||
if len(values) >= 2:
|
||||
total_gap_db = 10 * np.log10(values[-1] / shannon_limit)
|
||||
ax.text(len(stages) / 2, max(values) * 0.85,
|
||||
f'Remaining gap to Shannon: {total_gap_db:.1f} dB',
|
||||
ha='center', fontsize=12, fontweight='bold',
|
||||
bbox=dict(boxstyle='round,pad=0.3', facecolor='lightyellow', alpha=0.8))
|
||||
|
||||
ax.set_xticks(x)
|
||||
ax.set_xticklabels(stages, fontsize=11)
|
||||
ax.set_ylabel(r'DE Threshold ($\lambda_s^*$, photons/slot)')
|
||||
ax.set_title('Shannon Limit Roadmap: Threshold Progression')
|
||||
ax.set_ylim(0, max(values) * 1.2)
|
||||
ax.grid(True, alpha=0.2, axis='y')
|
||||
|
||||
fig.savefig(OUT_DIR / 'threshold_progression.png')
|
||||
plt.close(fig)
|
||||
print(f' Saved threshold_progression.png')
|
||||
|
||||
|
||||
def plot_sc_threshold_bars(data_sc):
|
||||
"""SC threshold vs uncoupled threshold bar chart."""
|
||||
if not data_sc:
|
||||
return
|
||||
|
||||
fig, ax = plt.subplots(figsize=(8, 5))
|
||||
|
||||
shannon_limit = data_sc.get('shannon_limit', 0.47)
|
||||
|
||||
thresholds = {}
|
||||
if 'uncoupled_thresholds' in data_sc:
|
||||
ut = data_sc['uncoupled_thresholds']
|
||||
thresholds['Original\n(offset)'] = (float(ut['original_offset']), '#d62728')
|
||||
thresholds['DE-opt\n(offset)'] = (float(ut['optimized_offset']), '#ff7f0e')
|
||||
thresholds['DE-opt\n(normalized)'] = (float(ut['optimized_normalized']), '#2ca02c')
|
||||
if 'sc_thresholds' in data_sc:
|
||||
st = data_sc['sc_thresholds']
|
||||
thresholds['SC\noriginal'] = (float(st['sc_original']), '#17becf')
|
||||
thresholds['SC\nDE-opt'] = (float(st['sc_optimized']), '#9467bd')
|
||||
|
||||
names = list(thresholds.keys())
|
||||
vals = [v[0] for v in thresholds.values()]
|
||||
colors = [v[1] for v in thresholds.values()]
|
||||
|
||||
bars = ax.bar(names, vals, color=colors, width=0.55, edgecolor='white', linewidth=1.5)
|
||||
|
||||
for bar, val in zip(bars, vals):
|
||||
ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.05,
|
||||
f'{val:.2f}', ha='center', va='bottom', fontweight='bold', fontsize=12)
|
||||
|
||||
ax.axhline(y=shannon_limit, color='black', linestyle='--', linewidth=1.5, alpha=0.6)
|
||||
ax.text(len(names) - 0.5, shannon_limit + 0.05,
|
||||
f'Shannon ({shannon_limit})', fontsize=10, ha='right', style='italic', alpha=0.7)
|
||||
|
||||
ax.set_ylabel(r'DE Threshold ($\lambda_s^*$, photons/slot)')
|
||||
ax.set_title('SC-LDPC Threshold Saturation Effect')
|
||||
ax.set_ylim(0, max(vals) * 1.25)
|
||||
ax.grid(True, alpha=0.2, axis='y')
|
||||
|
||||
fig.savefig(OUT_DIR / 'sc_threshold_comparison.png')
|
||||
plt.close(fig)
|
||||
print(f' Saved sc_threshold_comparison.png')
|
||||
|
||||
|
||||
def plot_sc_fer_comparison(data_sc):
|
||||
"""FER comparison: SC-LDPC vs uncoupled."""
|
||||
if not data_sc or 'fer_comparison' not in data_sc:
|
||||
return
|
||||
|
||||
fig, ax = plt.subplots(figsize=(8, 5.5))
|
||||
|
||||
fer_data = data_sc['fer_comparison']
|
||||
lam_s_points = fer_data['lam_s_points']
|
||||
sc_fer = fer_data['sc_fer']
|
||||
|
||||
# SC-LDPC FER
|
||||
fer_vals = [sc_fer[str(l)]['fer'] for l in lam_s_points]
|
||||
floor = 5e-3
|
||||
ax.semilogy(lam_s_points, [max(f, floor) for f in fer_vals],
|
||||
color='#9467bd', marker='D', markersize=9, linewidth=2.5,
|
||||
label=f'SC-LDPC (L={fer_data["params"]["L"]}, windowed)',
|
||||
markeredgecolor='white', markeredgewidth=0.8)
|
||||
|
||||
ax.set_xlabel(r'Signal photons/slot ($\lambda_s$)')
|
||||
ax.set_ylabel('Frame Error Rate (FER)')
|
||||
L = fer_data['params']['L']
|
||||
ax.set_title(f'SC-LDPC FER (L={L}, w=2, Z=32, normalized alpha=0.875)')
|
||||
ax.legend(loc='upper right', framealpha=0.9)
|
||||
ax.set_xlim(1.5, 10.5)
|
||||
ax.set_ylim(floor / 2, 1.1)
|
||||
ax.grid(True, alpha=0.3, which='both')
|
||||
|
||||
fig.savefig(OUT_DIR / 'sc_fer.png')
|
||||
plt.close(fig)
|
||||
print(f' Saved sc_fer.png')
|
||||
|
||||
|
||||
def main():
|
||||
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
data = load_results()
|
||||
@@ -311,6 +486,15 @@ def main():
|
||||
else:
|
||||
print(' Skipping Z=128 comparison (data/de_results_z128.json not found)')
|
||||
|
||||
# SC-LDPC plots
|
||||
data_sc = load_sc_results()
|
||||
if data_sc is not None:
|
||||
plot_sc_threshold_bars(data_sc)
|
||||
plot_sc_fer_comparison(data_sc)
|
||||
plot_threshold_progression(data, data_sc)
|
||||
else:
|
||||
print(' Skipping SC-LDPC plots (data/sc_ldpc_results.json not found)')
|
||||
|
||||
print(f'\nAll plots saved to {OUT_DIR}/')
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user