serialize the host netlist unit by unit and instantiate each generation from the emitted bytes
0654a8d | """Emit the TikZ for the deviation-rate figure from the recorded run. | |
| The figure plots, against the noise level, the measured probability that a step | |
| of the levelized host deviates from the exact orbit and the union bound of the | |
| random-perturbation proposition. Both axes are read from | |
| paper/runs/paper_noise_curve.json. | |
| python src/make_figures.py > /tmp/curve.tex | |
| """ | |
| import json | |
| import math | |
| import os | |
| import sys | |
| REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
| def load(name): | |
| return json.load(open(os.path.join(REPO, "paper", "runs", name))) | |
| def group(n): | |
| return f"{n:,}".replace(",", "{,}") | |
| def sci(x, digits=2): | |
| """LaTeX scientific notation.""" | |
| if x == 0: | |
| return "$0$" | |
| e = math.floor(math.log10(abs(x))) | |
| m = x / 10 ** e | |
| if e == 0: | |
| return f"${m:.{digits}f}$" | |
| return rf"${m:.{digits}f}\times 10^{{{e}}}$" | |
| def omega_table(): | |
| d = load("paper_noise_omega.json") | |
| rows = [] | |
| for r in d["rows"]: | |
| if r["complete"]: | |
| outcome = "emitted the whole instance" | |
| elif r["first_wrong_byte"] is not None: | |
| outcome = (f"a wrong byte after {group(r['first_wrong_byte'])} " | |
| f"correct") | |
| elif r["halted"]: | |
| outcome = "the halt bit was set" | |
| else: | |
| outcome = "the budget was exhausted" | |
| rows.append(f"${r['sigma']:.2f}$ & {group(r['steps'])} & " | |
| f"{group(r['bytes'])} & {outcome}\\\\") | |
| body = "\n".join(rows) | |
| return rf"""\begin{{table}}[ht] | |
| \centering\small | |
| \begin{{tabular}}{{@{{}}lrrl@{{}}}} | |
| \toprule | |
| $s$ & Steps executed & Bytes emitted & Outcome\\ | |
| \midrule | |
| {body} | |
| \bottomrule | |
| \end{{tabular}} | |
| \caption{{The self-reproducing instance $\Omega^\ast$ on $\Lev(\Nhost)$ with | |
| additive Gaussian read noise of standard deviation $s$ applied independently to | |
| every pre-activation of every layer at every step, thresholded at | |
| $-\tfrac12$. A run ends when it emits a byte that differs from | |
| $\ser(\Omega^\ast)$, when the halt bit is set, or when the whole instance has | |
| been emitted. The target is {group(d['target_bytes'])} bytes.}} | |
| \label{{tab:noise}} | |
| \end{{table}}""" | |
| def leak_table(): | |
| d = load("paper_noise_leak.json") | |
| rows = [] | |
| for r in d["rows"]: | |
| first = (f"deviates at step {group(r['first_deviation'])}" | |
| if r["first_deviation"] else | |
| f"exact for {group(d['steps'])} steps") | |
| rows.append(f"{sci(r['epsilon'])} & {sci(r['gamma'])} & " | |
| f"${r['worst_bound']:.3f}$ & " | |
| f"{'yes' if r['certified_by_corollary'] else 'no'} & " | |
| f"{first}\\\\") | |
| body = "\n".join(rows) | |
| return rf"""\begin{{table}}[ht] | |
| \centering\small | |
| \begin{{tabular}}{{@{{}}lllll@{{}}}} | |
| \toprule | |
| $\varepsilon$ & $\gamma$ & $\max_{{\ell,i}}(\varepsilon k+\gamma(n-k))$ & | |
| Certified & Outcome\\ | |
| \midrule | |
| {body} | |
| \bottomrule | |
| \end{{tabular}} | |
| \caption{{Static weight error on $\Lev(\Nhost)$: each programmed weight is | |
| perturbed uniformly in $[-\varepsilon,\varepsilon]$ and each zero entry | |
| uniformly in $[-\gamma,\gamma]$, and the perturbed map is run beside the exact | |
| one from the initial state of the self-describing instance until their states | |
| differ. The widest layer has $n_{{\max}}={group(d['widest_layer'])}$ and the | |
| largest effective fan-in is {d['largest_fanin']}, so | |
| Corollary~\ref{{cor:mismatch}} certifies exactly the rows whose third column is | |
| below $\tfrac12$.}} | |
| \label{{tab:leak}} | |
| \end{{table}}""" | |
| def curve_table(): | |
| d = load("paper_noise_curve.json") | |
| rows = [] | |
| for r in d["rows"]: | |
| rate = (sci(r["empirical_rate_per_step"]) | |
| if r["empirical_rate_per_step"] else "none seen") | |
| ratio = f"${r['looseness']:.1f}$" if r["looseness"] else "---" | |
| med = (group(r["median_first_deviation"]) | |
| if r["median_first_deviation"] else "---") | |
| rows.append(f"${r['sigma']:.2f}$ & {r['deviated']}/{r['copies']} & " | |
| f"{med} & {group(r['step_exposure'])} & {rate} & " | |
| f"{sci(r['bound_rate_per_step'])} & {ratio}\\\\") | |
| body = "\n".join(rows) | |
| return rf"""\begin{{table}}[ht] | |
| \centering\small | |
| \begin{{tabular}}{{@{{}}lllrllr@{{}}}} | |
| \toprule | |
| $s$ & Deviated & Median step & Exposure & Measured rate & Bound $Mp(s)$ & | |
| Ratio\\ | |
| \midrule | |
| {body} | |
| \bottomrule | |
| \end{{tabular}} | |
| \caption{{The probability that one step of $\Lev(\Nhost)$ deviates from the | |
| exact orbit under read noise of standard deviation $s$. For each $s$, one | |
| noise-free copy and {d['rows'][0]['copies']} noisy copies were stepped together | |
| from the initial state of the self-describing instance and the full state | |
| compared after each step; the exposure is the number of step--copy pairs before | |
| a first deviation. The bound is that of Proposition~\ref{{prop:random}} with | |
| $M={group(d['M'])}$, and the last column is the ratio of the two. Below | |
| $s=0.08$ the bound is smaller than any rate this experiment could resolve.}} | |
| \label{{tab:curve}} | |
| \end{{table}}""" | |
| def speed_table(): | |
| d = load("paper_throughput.json") | |
| T = json.load(open(os.path.join(REPO, "paper", "runs", | |
| "paper_runs_reference.json")) | |
| )["selfrep"]["generations"][0]["steps"] | |
| order = [("reference", "integer reference, Definition 3.1"), | |
| ("net_cpu", "the netlist of $\\sigma$, unit by unit, one core"), | |
| ("net_gpu", "the netlist of $\\sigma$, unit by unit, GPU"), | |
| ("dense_gpu", "$\\Lev(\\Nhost)$ as dense matrices, GPU"), | |
| ("graph_gpu", "$\\Lev(\\Nhost)$ from its nonzero entries, GPU"), | |
| ("sparse_cpu", "$\\Lev(\\Nhost)$ from its nonzero entries, one core")] | |
| rows = [] | |
| for key, what in order: | |
| if key not in d: | |
| continue | |
| r = d[key]["steps_per_second"] | |
| gen = T / r | |
| span = (f"{gen:.1f} s" if gen < 90 else f"{gen / 60:.1f} min") | |
| rows.append(f"{what} & {group(round(r))} & {span}\\\\") | |
| body = "\n".join(rows) | |
| ent = d.get("dense_gpu", {}).get("entries", 0) | |
| nz = d.get("dense_gpu", {}).get("nonzero", 0) | |
| bw = d.get("dense_gpu", {}).get("bandwidth_gbs", 0) | |
| return rf"""\begin{{table}}[ht] | |
| \centering\small | |
| \begin{{tabular}}{{@{{}}lrr@{{}}}} | |
| \toprule | |
| Evaluator & Steps per second & One generation\\ | |
| \midrule | |
| {body} | |
| \bottomrule | |
| \end{{tabular}} | |
| \caption{{What one generation of $\Omega^\ast$ costs on each evaluator, measured | |
| on an otherwise idle machine over $20{{,}}000$ steps of the self-describing | |
| instance after a warm-up. The dense form reads all | |
| {group(ent)} matrix entries at every step, of which {group(nz)} are nonzero, | |
| which is {ent * 4 / 1e6:.0f}~MB and {bw:.0f}~GB/s, close to the memory bandwidth | |
| of the accelerator. Evaluating the same map from its nonzero entries replaces | |
| that traffic by {group(nz)} gathers and is four times faster once the level plan | |
| is captured as a single CUDA graph, which issues one operation per step in place | |
| of some hundreds; on one processor core, where there is no launch to amortise, | |
| the gathers cost more than the dense arithmetic saves. The netlist carries | |
| $50{{,}}250$ weighted predecessor entries against the {group(nz)} of its | |
| levelization and is faster in that proportion on either processor.}} | |
| \label{{tab:speed}} | |
| \end{{table}}""" | |
| def main() -> int: | |
| if len(sys.argv) > 1 and sys.argv[1] == "speed": | |
| print(speed_table()) | |
| return 0 | |
| if len(sys.argv) > 1 and sys.argv[1] == "tables": | |
| print(omega_table()) | |
| print() | |
| print(curve_table()) | |
| print() | |
| print(leak_table()) | |
| return 0 | |
| d = json.load(open(os.path.join(REPO, "paper", "runs", | |
| "paper_noise_curve.json"))) | |
| rows = [r for r in d["rows"]] | |
| xs = [r["sigma"] for r in rows] | |
| emp = [(r["sigma"], r["empirical_rate_per_step"]) for r in rows | |
| if r["empirical_rate_per_step"] > 0] | |
| bnd = [(r["sigma"], r["bound_rate_per_step"]) for r in rows] | |
| lo = math.floor(min([math.log10(y) for _, y in emp] | |
| + [math.log10(y) for _, y in bnd if y > 0])) | |
| hi = math.ceil(max([math.log10(y) for _, y in emp] | |
| + [math.log10(y) for _, y in bnd])) | |
| x0, x1 = min(xs), max(xs) | |
| W, H = 105.0, 55.0 | |
| def px(s): | |
| return W * (s - x0) / (x1 - x0) | |
| def py(v): | |
| return H * (math.log10(v) - lo) / (hi - lo) | |
| out = [] | |
| a = out.append | |
| a(r"\begin{figure}[ht]") | |
| a(r"\centering") | |
| a(r"\begin{tikzpicture}[font=\small, x=1mm, y=1mm]") | |
| a(rf"\draw[->,>=stealth] (-2,0) -- ({W + 6:.1f},0) " | |
| rf"node[right,font=\footnotesize] {{$s$}};") | |
| a(rf"\draw[->,>=stealth] (-2,-2) -- (-2,{H + 6:.1f});") | |
| for e in range(lo, hi + 1): | |
| y = H * (e - lo) / (hi - lo) | |
| a(rf"\draw[black!20] (-2,{y:.2f}) -- ({W:.2f},{y:.2f});") | |
| a(rf"\node[left,font=\scriptsize] at (-2.5,{y:.2f}) " | |
| rf"{{$10^{{{e}}}$}};") | |
| for s in xs: | |
| a(rf"\draw (({px(s):.2f}),0) -- ({px(s):.2f},-1.2);") | |
| a(rf"\node[below,font=\scriptsize] at ({px(s):.2f},-1.2) " | |
| rf"{{${s:g}$}};") | |
| a(rf"\node[rotate=90,font=\footnotesize] at (-12,{H / 2:.1f}) " | |
| rf"{{probability per step}};") | |
| pts = " -- ".join(f"({px(s):.2f},{py(v):.2f})" for s, v in bnd) | |
| a(rf"\draw[thick,dashed] {pts};") | |
| for s, v in bnd: | |
| a(rf"\fill ({px(s):.2f},{py(v):.2f}) circle (0.7mm);") | |
| pts = " -- ".join(f"({px(s):.2f},{py(v):.2f})" for s, v in emp) | |
| a(rf"\draw[thick] {pts};") | |
| for s, v in emp: | |
| a(rf"\draw[fill=white,thick] ({px(s):.2f},{py(v):.2f}) " | |
| rf"circle (0.8mm);") | |
| a(rf"\draw[dashed,black!50] ({px(x0):.2f},{py(1.0):.2f}) -- " | |
| rf"({W:.2f},{py(1.0):.2f});") | |
| a(rf"\node[right,font=\scriptsize] at ({W - 30:.1f},{py(1.0) + 3:.2f}) " | |
| rf"{{bound vacuous above here}};") | |
| a(rf"\node[font=\scriptsize,anchor=west] at (4,{py(bnd[-1][1]) - 5:.2f}) " | |
| rf"{{union bound $Mp(s)$}};") | |
| a(rf"\node[font=\scriptsize,anchor=west] at (20,{py(emp[0][1]) - 5:.2f}) " | |
| rf"{{measured}};") | |
| a(r"\end{tikzpicture}") | |
| a(r"\caption{The probability that one step of $\Lev(\Nhost)$ deviates from " | |
| r"the exact orbit under additive Gaussian read noise of standard " | |
| r"deviation $s$, measured by running noise-free and noisy copies together " | |
| r"and comparing the full state after every step, against the union bound " | |
| r"$Mp(s)$ of Proposition~\ref{prop:random} with $M=87{,}294$. Both curves " | |
| r"are the same quantity, the upper one as bounded above.}") | |
| a(r"\label{fig:curve}") | |
| a(r"\end{figure}") | |
| print("\n".join(out)) | |
| return 0 | |
| if __name__ == "__main__": | |
| sys.exit(main()) | |