PaperTool/workspace/resource_allocation/analysis/reference_plots.py
hc ced50ea2b0 feat(agent): add result-verifier for blind visual comparison
Root cause: test-runner was giving overly optimistic results due to:
1. Context bias - knew the implementation, tended to defend it
2. No actual visual comparison - just wrote 'ACCEPTABLE' without looking
3. No structural validation - accepted 35x scale differences as 'acceptable'

Solution:
- New result-verifier agent that performs blind visual comparison
- Strict pass/fail criteria for structural validation
- Updated test-runner to use result-verifier for each figure
- Clear guidelines: structural mismatches = FAIL, not ACCEPTABLE

Test result: verifier correctly identified Fig3 as FAIL with 7 specific issues:
- Wrong X-axis variable (channels vs power)
- Wrong Y-axis scale (5x difference)
- Wrong curve count (5 vs 4)
- etc.
2026-03-31 23:56:36 +08:00

213 lines
6.5 KiB
Python

"""
Reference plots for Resource Allocation for Text Semantic Communications
Generated from paper images for verification purposes.
Run: python reference_plots.py
Output: workspace/resource_allocation/analysis/reference_images/
"""
import matplotlib.pyplot as plt
import numpy as np
from pathlib import Path
from scipy.interpolate import PchipInterpolator
OUTPUT_DIR = Path("workspace/resource_allocation/analysis/reference_images")
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
def plot_figure_2():
"""
Figure 2: The semantic similarity for DeepSC
"""
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection="3d")
# Generate data
snr = np.linspace(-10, 20, 30)
k_n = np.linspace(0, 20, 30)
SNR, KN = np.meshgrid(snr, k_n)
# Approximate function for similarity
# Logistic-like function depending on SNR and k_n
z = 0.4 + 0.6 / (1 + np.exp(-0.3 * (SNR + 5)) * np.exp(-0.2 * (KN - 5)))
z = np.clip(z, 0.4, 1.0)
surf = ax.plot_surface(SNR, KN, z, cmap="viridis", edgecolor="none", alpha=0.9)
ax.set_xlabel("SNR, $\gamma_{n,m}$ (dB)")
ax.set_ylabel("$k_n$ (symbols/word)")
ax.set_zlabel(r"$\xi_{n,m}$")
ax.set_zlim(0.4, 1.0)
ax.view_init(elev=30, azim=225)
plt.savefig(OUTPUT_DIR / "fig2.png", dpi=150)
plt.close()
print("Generated: fig2.png")
def plot_figure_3():
"""
Figure 3: The S-SE of the semantic-aware network with different models
"""
M = np.arange(1, 11)
# Approximate values from visual inspection
proposed = np.array([0.24, 0.48, 0.72, 0.96, 1.18, 1.20, 1.20, 1.20, 1.20, 1.20])
conv_k3 = np.zeros(10)
conv_k5 = np.array([0.20, 0.39, 0.58, 0.77, 0.94, 0.96, 0.97, 0.97, 0.97, 0.97])
conv_k7 = np.array([0.14, 0.28, 0.42, 0.56, 0.70, 0.70, 0.70, 0.70, 0.70, 0.70])
conv_k9 = np.array([0.11, 0.22, 0.33, 0.44, 0.54, 0.54, 0.54, 0.54, 0.54, 0.54])
plt.figure(figsize=(8, 6))
plt.plot(M, proposed, "rd-", label="Proposed model")
plt.plot(M, conv_k3, "ko-", label="Conventional model, $k_n = 3$", fillstyle="none")
plt.plot(M, conv_k5, "k+-", label="Conventional model, $k_n = 5$")
plt.plot(M, conv_k7, "k*-", label="Conventional model, $k_n = 7$")
plt.plot(M, conv_k9, "kx-", label="Conventional model, $k_n = 9$")
plt.xlabel("Number of channels, $M$")
plt.ylabel("S-SE, $\Phi$ (suts/s/Hz) $\\times (I/L)$")
plt.xticks(np.arange(1, 11))
plt.yticks(np.arange(0, 1.5, 0.2))
plt.grid(True)
plt.legend(loc="lower right")
plt.xlim(1, 10)
plt.ylim(0, 1.3)
plt.savefig(OUTPUT_DIR / "fig3.png", dpi=150)
plt.close()
print("Generated: fig3.png")
def plot_figure_4a():
"""
Figure 4(a): The S-SE versus the number of channels
"""
M = np.arange(1, 11)
# Approximate values from visual inspection
semantic = np.array([0.24, 0.48, 0.72, 0.96, 1.18, 1.20, 1.20, 1.20, 1.20, 1.20])
ideal = np.array([0.21, 0.40, 0.55, 0.68, 0.79, 0.82, 0.84, 0.85, 0.86, 0.87])
g5 = np.array([0.13, 0.26, 0.37, 0.47, 0.56, 0.58, 0.59, 0.60, 0.60, 0.60])
g4 = np.array([0.12, 0.23, 0.31, 0.39, 0.46, 0.48, 0.49, 0.49, 0.50, 0.50])
plt.figure(figsize=(8, 6))
plt.plot(M, semantic, "rd-", label="Semantic")
plt.plot(M, ideal, "ko-", label="Ideal", fillstyle="none")
plt.plot(M, g5, "k*-.", label="5G")
plt.plot(M, g4, "ks--", label="4G", fillstyle="none")
plt.xlabel("Number of channels, $M$")
plt.ylabel("S-SE, $\Phi$ (suts/s/Hz) $\\times (I/L)$")
plt.xticks(np.arange(1, 11))
plt.yticks(np.arange(0, 1.6, 0.2))
plt.grid(True)
plt.legend(loc="upper left")
plt.xlim(1, 10)
plt.ylim(0, 1.4)
plt.savefig(OUTPUT_DIR / "fig4a.png", dpi=150)
plt.close()
print("Generated: fig4a.png")
def plot_figure_4b():
"""
Figure 4(b): The S-SE versus the transmit power
"""
p_n = np.arange(-40, 25, 5)
# Approximate function to match shapes
# Semantic: logistic curve
semantic = 1.21 / (1 + np.exp(-0.25 * (p_n + 5)))
# Ideal: mostly linear in higher dBm, slower in lower
# Use Shannon approx log2(1 + SNR)
snr_linear_ideal = 10 ** ((p_n - 10) / 10) # arbitrary scaling to match
ideal = 0.15 * np.log2(1 + 10 ** ((p_n + 15) / 10))
ideal = np.clip(ideal, 0, 1.35)
# 5G and 4G: similar to semantic but lower cap and shifted
g5 = 0.7 / (1 + np.exp(-0.15 * (p_n - 5)))
g4 = 0.68 / (1 + np.exp(-0.15 * (p_n - 8)))
# Slight manual adjustments to match visual points
ideal = np.interp(
p_n,
[-40, -30, -20, -10, 0, 10, 20, 23],
[0.0, 0.01, 0.05, 0.15, 0.38, 0.72, 1.15, 1.32],
)
plt.figure(figsize=(8, 6))
plt.plot(p_n, semantic, "rd-", label="Semantic")
plt.plot(p_n, ideal, "ko-", label="Ideal", fillstyle="none")
plt.plot(p_n, g5, "k*-.", label="5G")
plt.plot(p_n, g4, "ks--", label="4G", fillstyle="none")
plt.xlabel("Transmit power, $p_n$ (dBm)")
plt.ylabel("S-SE, $\Phi$ (suts/s/Hz) $\\times (I/L)$")
plt.xticks([-40, -30, -20, -10, 0, 10, 23])
plt.yticks(np.arange(0, 1.6, 0.2))
plt.grid(True)
plt.legend(loc="upper left")
plt.xlim(-40, 23)
plt.ylim(0, 1.4)
plt.savefig(OUTPUT_DIR / "fig4b.png", dpi=150)
plt.close()
print("Generated: fig4b.png")
def plot_figure_4c():
"""
Figure 4(c): The S-SE versus the transforming factor
"""
mu = np.arange(18, 42, 2)
# Values extracted from plot visually
semantic = np.ones_like(mu) * 1.18
# Conventional decrease roughly as 1/mu
# Ideal at mu=18 is ~1.78. 1.78 * 18 = 32.04
ideal = 32.04 / mu
# 5G at mu=18 is ~1.25. 1.25 * 18 = 22.5
g5 = 22.5 / mu
# 4G at mu=18 is ~1.02. 1.02 * 18 = 18.36
g4 = 18.36 / mu
plt.figure(figsize=(8, 6))
plt.plot(mu, semantic, "rd-", label="Semantic")
plt.plot(mu, ideal, "ko-", label="Ideal", fillstyle="none")
plt.plot(mu, g5, "k*-.", label="5G")
plt.plot(mu, g4, "ks--", label="4G", fillstyle="none")
plt.xlabel("Transforming factor, $\mu$ (bits/word)")
plt.ylabel("S-SE, $\Phi$ (suts/s/Hz) $\\times (I/L)$")
plt.xticks(np.arange(18, 42, 2))
plt.yticks(np.arange(0.4, 2.0, 0.2))
plt.grid(True)
plt.legend(loc="upper right")
plt.xlim(18, 40)
plt.ylim(0.4, 1.8)
plt.savefig(OUTPUT_DIR / "fig4c.png", dpi=150)
plt.close()
print("Generated: fig4c.png")
def main():
"""Generate all reference plots."""
print("Generating reference plots...")
plot_figure_2()
plot_figure_3()
plot_figure_4a()
plot_figure_4b()
plot_figure_4c()
print(f"\nAll plots saved to: {OUTPUT_DIR}")
if __name__ == "__main__":
main()