diff --git a/code/make_tables.py b/code/make_tables.py index 98d1a3c..79ede7c 100644 --- a/code/make_tables.py +++ b/code/make_tables.py @@ -36,8 +36,19 @@ def f3(x: str) -> str: return "--" if math.isnan(v) else f"{v:.3f}" -def cell(x: str, bold: bool) -> str: - s = f3(x) +def f4(x: str) -> str: + """Four decimals, for a column whose values sit against the + random-guess level and would otherwise all print as 1.000 while the + body quotes their distance from it in units of 1e-4.""" + try: + v = float(x) + except (TypeError, ValueError): + return "--" + return "--" if math.isnan(v) else f"{v:.4f}" + + +def cell(x: str, bold: bool, wide: bool = False) -> str: + s = f4(x) if wide else f3(x) if s == "--": return "--" return rf"$\mathbf{{{s}}}$" if bold else f"${s}$" @@ -59,6 +70,8 @@ def compare_table(): b = r["scheme"] == "proposed" if r["scheme"] == "oma_plain" and f3(r["jam0_ser"]) == "--": r["jam0_ser"] = oma_jam + # four decimals would still print 1.0000 here, so the column + # stays at three and the caption names the chance level cells = [cell(r[k], b) for k in ("legit_ser", "eve_out", "eve_in", "jam0_ser")] print(f"{NAME[r['scheme']]} & " + " & ".join(cells) + r" \\") @@ -96,7 +109,8 @@ def refresh_tables(): f = (lambda t: r"\mathbf{" + t + "}") if b else (lambda t: t) print(f"{name} & ${f(format(float(r['legit']), '.3f'))}$ & " f"${f(format(float(r['eve']), '.4f'))}$ & " - f"${f(format(float(r['entropy_bits']), '.1f'))}$~bits" + r" \\") + # the unit lives in the header, not in every cell + f"${f(format(float(r['entropy_bits']), '.1f'))}$" + r" \\") print() print("% Table: known plaintext across a refresh (from refresh_kpa.csv)") rows = {r["n_frames"]: r for r in csv.DictReader(open(DATA / "refresh_kpa.csv"))} @@ -105,8 +119,10 @@ def refresh_tables(): + " & ".join(f"${k}$" for k in keep) + r" \\") for lbl, key in (("Same block", "ser_same_block"), ("Next block", "ser_next_block")): + w = ".4f" if key == "ser_next_block" else ".3f" print(f"{lbl} & " - + " & ".join(f"${float(rows[k][key]):.3f}$" for k in keep) + + " & ".join("$" + format(float(rows[k][key]), w) + "$" + for k in keep) + r" \\") diff --git a/code/replot_security.py b/code/replot_security.py index 6279311..5bd9ec8 100644 --- a/code/replot_security.py +++ b/code/replot_security.py @@ -34,21 +34,26 @@ FIG.mkdir(exist_ok=True) plt.rcParams.update({ "font.family": "serif", "font.serif": ["DejaVu Serif", "Times New Roman"], - "font.size": 9, - "axes.labelsize": 9, - "legend.fontsize": 6.6, - "xtick.labelsize": 8, - "ytick.labelsize": 8, + # The manuscript includes each result figure at 0.70 of a 3.455 in + # column while the canvas is 3.15 in, a printed scale of 0.768. Every + # size below is therefore pre-divided by that scale so the PRINTED + # sizes are 9 pt labels, 8 pt ticks and a 6.6 pt legend. Change the + # include width and these must change with it. + "font.size": 10.4, + "axes.labelsize": 10.4, + "legend.fontsize": 7.6, + "xtick.labelsize": 9.2, + "ytick.labelsize": 9.2, "axes.grid": True, "grid.linestyle": "--", "grid.linewidth": 0.4, "grid.alpha": 0.6, - "lines.linewidth": 1.3, - "lines.markersize": 4.5, + "lines.linewidth": 1.5, + "lines.markersize": 5.2, "figure.figsize": (3.15, 2.36), "pdf.fonttype": 42, }) -AXES_RECT = dict(left=0.185, right=0.965, top=0.955, bottom=0.195) +AXES_RECT = dict(left=0.205, right=0.970, top=0.955, bottom=0.215) C_LEGIT = "#c0392b" C_EVE = "#2c5fa8" @@ -178,12 +183,18 @@ def main_legit(snr_db="10"): def place_legend(ax, cands=("lower left", "center left", "center right", "lower center", "upper right", "upper center", "center", "lower right"), - sizes=(6.6, 6.2, 5.8, 5.4, 5.0)): + sizes=(7.6, 7.2, 6.8, 6.4, 6.0)): """Choose the location and font size whose box the fewest curve points fall inside, scored on rendered geometry rather than guessed from the data. The size sweep is what makes a long label set placeable: a five-entry legend of full scheme names has no clear corner at the - default size on every figure.""" + default size on every figure. + + The axes rectangle is applied first, because save() enforces the same + test after applying it. Scoring the default layout and then checking + a different one is how a placement that looked clear here failed + there.""" + ax.figure.subplots_adjust(**AXES_RECT) best = None for size in sizes: for loc in cands: @@ -227,8 +238,10 @@ def fig_snr(): label=LBL["eve_pub"]) ax.semilogy(x, col(r, "eve_wrong"), color=C_EVE, marker="s", ls="--", label=LBL["eve_key"]) - ax.plot(x, col(r, "chance"), color=C_CH, ls="-.", lw=0.9, - label=LBL["chance"]) + # the chance level lies within 3.5e-4 of the wrong-key curve, so it is + # drawn for reference but left out of the legend, which the caption + # names instead; five long entries leave this figure no clear corner + ax.plot(x, col(r, "chance"), color=C_CH, ls="-.", lw=0.9) ax.set_xlabel("SNR (dB)") ax.set_ylabel("SER") ax.set_xlim(min(x), max(x)) @@ -284,7 +297,7 @@ def fig_jam(): ax.set_xlabel("JSR (dB)") ax.set_ylabel("SER") ax.set_xlim(min(x), max(x)) - ax.set_ylim(0.8 * nojam, 1.02) + ax.set_ylim(0.0, 1.02) # keep the reference line off the spine place_legend(ax) save(fig, "fig_sec_jam") @@ -327,7 +340,7 @@ def fig_brute(): ax.axhline(legit, color=C_OMA, ls=":", lw=0.9, label=LBL["legit"]) ax.set_xlabel("Number of key guesses $K$") ax.set_ylabel("Eavesdropper SER") - ax.set_ylim(0.8 * legit, 1.05) + ax.set_ylim(0.0, 1.05) # keep the reference line off the spine place_legend(ax) save(fig, "fig_sec_brute") diff --git a/fig/fig_sec_brute.pdf b/fig/fig_sec_brute.pdf index 7ff5b0d..809c1d7 100644 Binary files a/fig/fig_sec_brute.pdf and b/fig/fig_sec_brute.pdf differ diff --git a/fig/fig_sec_jam.pdf b/fig/fig_sec_jam.pdf index b3f6172..b6c0194 100644 Binary files a/fig/fig_sec_jam.pdf and b/fig/fig_sec_jam.pdf differ diff --git a/fig/fig_sec_keylen.pdf b/fig/fig_sec_keylen.pdf index abd44b2..f43dcc5 100644 Binary files a/fig/fig_sec_keylen.pdf and b/fig/fig_sec_keylen.pdf differ diff --git a/fig/fig_sec_kpa.pdf b/fig/fig_sec_kpa.pdf index 552c3a6..0fd0e10 100644 Binary files a/fig/fig_sec_kpa.pdf and b/fig/fig_sec_kpa.pdf differ diff --git a/fig/fig_sec_real.pdf b/fig/fig_sec_real.pdf index 11c92fc..a6b10ee 100644 Binary files a/fig/fig_sec_real.pdf and b/fig/fig_sec_real.pdf differ diff --git a/fig/fig_sec_sens.pdf b/fig/fig_sec_sens.pdf index 64bf7a9..960be1d 100644 Binary files a/fig/fig_sec_sens.pdf and b/fig/fig_sec_sens.pdf differ diff --git a/fig/fig_sec_snr.pdf b/fig/fig_sec_snr.pdf index df13d14..2bc7b6d 100644 Binary files a/fig/fig_sec_snr.pdf and b/fig/fig_sec_snr.pdf differ