phanerozoic's picture
Answer the referee report and clarify the paper: notation, an example, a conclusion and the organism's records
7bd2bb1
Raw History Blame Contribute Delete
66.1 kB
"""Perturbation experiments on the levelized host and on the amplified host."""
from __future__ import annotations
import argparse
import hashlib
import json
import math
import os
import subprocess
import sys
import tempfile
import time
from typing import Dict, List, Tuple
from host import (
encode,
M_P,
M_STAR,
HALT_PC,
Tape,
describe,
inst,
read_host,
ser,
tau_star,
STATE_LAYOUT,
IO_CELLS,
)
from paper import levelize, parse_sigma, write_flat
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
def runs_path(name: str) -> str:
d = os.path.join(REPO, "paper", "runs")
os.makedirs(d, exist_ok=True)
return os.path.join(d, name)
def union_bound(s: float, M: int, T: int) -> float:
return T * M * math.erfc(1.0 / (2.0 * math.sqrt(2.0) * s))
class Devices:
"""The tape device applied to every copy of a batched state."""
def __init__(self, L, tapes, device):
import torch
from host import Tape
self.torch = torch
self.L = L
self.device = device
self.n = len(tapes)
self.tapes = [Tape(t) for t in tapes]
self.done = [False] * self.n
self.shadow = [[0] * 256 for _ in range(self.n)]
self.cell_idx = torch.tensor(
[9 + j * 8 + k for j in L._DEVCELLS for k in range(8)],
device=device)
self.pow2 = torch.tensor([1 << (7 - k) for k in range(8)],
device=device)
self.shift = torch.tensor([7 - k for k in range(8)])
self.ncell = len(L._DEVCELLS)
def step(self, v):
"""Apply the device to every copy; return the halt flags."""
torch = self.torch
vals = (v[:, self.cell_idx].reshape(self.n, self.ncell, 8)
* self.pow2).sum(-1).to(torch.int64).cpu().tolist()
halted = (v[:, 8] >= 0.5).cpu().tolist()
for c in range(self.n):
sh = self.shadow[c]
row = vals[c]
for ci, j in enumerate(self.L._DEVCELLS):
sh[j] = row[ci]
if not self.done[c]:
self.tapes[c].apply(sh) # the halting step is a step
for ci, j in enumerate(self.L._DEVCELLS):
row[ci] = sh[j]
for c in range(self.n):
self.done[c] = self.done[c] or halted[c]
new = torch.tensor(vals, dtype=torch.int64)
bits = ((new.unsqueeze(-1) >> self.shift) & 1).float()
v[:, self.cell_idx] = bits.reshape(self.n, self.ncell * 8).to(self.device)
return halted
# ---------------------------------------------------------------------------
def run_omega(args) -> int:
"""The self-reproducing instance under read noise, to completion."""
import torch
from host import M_STAR, LevEvaluator, read_host, ser, tau_star
sigma = read_host()
# the two forms of Lev(N_host) compute the same pre-activations, and this
# run is long enough that the faster one is worth having
L = LevEvaluator(sigma, device=args.device, dense=False)
M = L.info["size"]
tau = tau_star(sigma, M_STAR)
target = ser(sigma, bytes(M_STAR), tau)
print(f"[omega] {L.info['layers']} layers, M = {M:,}, target "
f"{len(target):,} bytes", flush=True)
rows = []
for s, budget in ((0.05, 1 << 21), (0.10, 50000), (0.15, 50000),
(0.20, 50000)):
gen = torch.Generator(device=args.device).manual_seed(20260910)
v = L._vec(0, M_STAR).unsqueeze(0).to(args.device)
D = Devices(L, [tau], args.device)
steps = 0
wrong = None
halted = False
t0 = time.perf_counter()
while steps < budget:
v = L.step_noisy(v, s, gen)
steps += 1
before = len(D.tapes[0].out)
halted = D.step(v)[0]
out = D.tapes[0].out
if len(out) > before:
k = len(out) - 1
if k >= len(target) or out[k] != target[k]:
wrong = k
break
if halted:
break
if steps % 100000 == 0:
print(f" s={s:.2f}: {steps:,} steps, {len(out):,} bytes "
f"({steps / (time.perf_counter() - t0):.0f}/s)",
flush=True)
dt = time.perf_counter() - t0
emitted = len(D.tapes[0].out)
complete = emitted == len(target) and wrong is None
outcome = ("emitted the whole instance" if complete else
f"a wrong byte after {wrong} correct" if wrong is not None
else "the halt bit was set" if halted
else "the budget was exhausted")
rows.append({"sigma": s, "steps": steps, "bytes": emitted,
"complete": complete, "first_wrong_byte": wrong,
"halted": halted, "seconds": dt,
"bound": union_bound(s, M, max(steps, 1))})
print(f" s={s:.2f}: {steps:,} steps, {emitted:,} bytes, "
f"{outcome} ({dt / 60:.1f} min)", flush=True)
json.dump({"M": M, "target_bytes": len(target), "rows": rows},
open(runs_path("paper_noise_omega.json"), "w"), indent=1)
return 0
# ---------------------------------------------------------------------------
def run_curve(args) -> int:
"""Empirical probability of deviation against the union bound."""
import torch
from host import M_P, LevEvaluator, describe, read_host
sigma = read_host()
L = LevEvaluator(sigma, device=args.device, dense=True)
M = sum(int(W.shape[0]) for W in L.W)
r_H = describe(sigma)
B = args.batch
cap = args.steps
print(f"[curve] M = {M:,}; {B} noisy copies per level, {cap:,} steps each",
flush=True)
grid = [float(x) for x in args.grid.split(",")]
rows = []
for s in grid:
# below 0.08 the bound is under 1e-11 per step, far below anything this
# experiment can resolve, so a short run suffices to record that
# nothing was seen
cap = args.steps if s >= 0.08 else min(args.steps, 3000)
gen = torch.Generator(device=args.device).manual_seed(7 + int(1000 * s))
v = L._vec(0, M_P).unsqueeze(0).to(args.device).repeat(B + 1, 1)
D = Devices(L, [r_H] * (B + 1), args.device)
first = [None] * B
t0 = time.perf_counter()
live = cap
for n in range(cap):
y = v
for W, b in zip(L.W, L.B):
pre = y @ W.T + b
noise = torch.randn(pre.shape, generator=gen,
device=pre.device) * s
noise[0] = 0.0 # copy 0 is noise free
y = ((pre + noise) >= -0.5).float()
v = y
diff = (v[1:] != v[0]).any(dim=1).cpu().tolist()
for i, d in enumerate(diff):
if d and first[i] is None:
first[i] = n + 1
if all(f is not None for f in first):
live = n + 1
break
D.step(v)
dt = time.perf_counter() - t0
deviated = [f for f in first if f is not None]
exposure = sum(f if f is not None else live for f in first)
rate = len(deviated) / exposure if exposure else 0.0
p = math.erfc(1.0 / (2.0 * math.sqrt(2.0) * s))
rows.append({"sigma": s, "copies": B, "steps": live,
"deviated": len(deviated),
"median_first_deviation":
sorted(deviated)[len(deviated) // 2] if deviated else None,
"step_exposure": exposure,
"empirical_rate_per_step": rate,
"bound_rate_per_step": M * p,
"looseness": (M * p / rate) if rate else None,
"first_deviation": first,
"seconds": dt})
print(f" s={s:.2f}: {len(deviated)}/{B} deviated within {live:,} "
f"steps; empirical {rate:.3e} per step, bound {M * p:.3e}, "
f"loose by {(M * p / rate) if rate else float('inf'):.1f}x "
f"({dt / 60:.1f} min)", flush=True)
json.dump({"M": M, "grid": grid, "rows": rows},
open(runs_path("paper_noise_curve.json"), "w"), indent=1)
return 0
# ---------------------------------------------------------------------------
def run_leak(args) -> int:
"""Static weight error including the leakage of the zero entries."""
import torch
from host import M_P, LevEvaluator, describe, read_host
sigma = read_host()
L = LevEvaluator(sigma, device=args.device, dense=True)
M = sum(int(W.shape[0]) for W in L.W)
nmax = max(int(W.shape[1]) for W in L.W)
kmax = max(int((W != 0).sum(dim=1).max()) for W in L.W)
r_H = describe(sigma)
print(f"[leak] M = {M:,}, widest layer {nmax}, largest fan-in {kmax}; "
f"Proposition 8.3 permits eps*k + gamma*(n-k) < 1/2", flush=True)
def worst_bound(eps, gam):
w = 0.0
for W in L.W:
k = (W != 0).sum(dim=1).float()
n = float(W.shape[1])
w = max(w, float((eps * k + gam * (n - k)).max()))
return w
def run(eps, gam, seed, steps):
gen = torch.Generator(device=args.device).manual_seed(seed)
Wp = []
for W in L.W:
u = torch.rand(W.shape, generator=gen, device=W.device) * 2 - 1
scale = torch.where(W != 0,
torch.tensor(eps, device=W.device),
torch.tensor(gam, device=W.device))
Wp.append(W + u * scale)
v = L._vec(0, M_P).unsqueeze(0).to(args.device).repeat(2, 1)
D = Devices(L, [r_H, r_H], args.device)
first = None
for n in range(steps):
y = v
for W, Wq, b in zip(L.W, Wp, L.B):
pre = torch.stack([y[0] @ W.T, y[1] @ Wq.T]) + b
y = (pre >= -0.5).float()
v = y
if bool((v[1] != v[0]).any()):
first = n + 1
break
D.step(v)
emitted = len(D.tapes[1].out)
del Wp
if args.device.startswith("cuda"):
torch.cuda.empty_cache()
return first, emitted
gamma_star = 0.5 / (nmax - 1)
rows = []
grid = [(0.0, 0.0), (0.0, gamma_star / 2), (0.0, gamma_star * 0.9),
(0.0, gamma_star * 1.5), (0.0, gamma_star * 4),
(0.0, gamma_star * 16), (0.0, gamma_star * 64),
(0.0, gamma_star * 256),
(1 / 1024, gamma_star / 4), (1 / 512, 0.0), (1 / 256, 0.0),
(1 / 64, 0.0), (1 / 16, 0.0), (1 / 4, 0.0)]
for eps, gam in grid:
t0 = time.perf_counter()
first, emitted = run(eps, gam, 4242, args.steps)
wb = worst_bound(eps, gam)
certified = wb < 0.5
rows.append({"epsilon": eps, "gamma": gam, "worst_bound": wb,
"certified_by_corollary": bool(certified),
"first_deviation": first,
"steps": first if first else args.steps,
"bytes": emitted, "seconds": time.perf_counter() - t0})
print(f" eps={eps:.3e} gamma={gam:.3e}: worst bound {wb:.4f}, "
f"certified {'yes' if certified else 'no '}; "
f"{'exact for all ' + format(args.steps, ',') + ' steps' if first is None else f'deviates at step {first:,}'}"
f" ({time.perf_counter() - t0:.0f}s)", flush=True)
json.dump({"M": M, "widest_layer": nmax, "largest_fanin": kmax,
"gamma_star": gamma_star, "steps": args.steps, "rows": rows},
open(runs_path("paper_noise_leak.json"), "w"), indent=1)
return 0
# ---------------------------------------------------------------------------
def run_margin(args) -> int:
"""The margin of Theorem 8.2 along the trajectory of the instance."""
import torch
from host import M_STAR, LevEvaluator, Tape, read_host, tau_star
sigma = read_host()
L = LevEvaluator(sigma, device=args.device, dense=True)
tau = tau_star(sigma, M_STAR)
mem, pc, dev, states, n = list(M_STAR), 0, Tape(tau), [], 0
want = args.states
while pc != HALT_PC:
if n % 2000 == 0 and len(states) < want:
states.append((pc, list(mem)))
A = mem[pc]
B = mem[(pc + 1) & 0xFF]
C = mem[(pc + 2) & 0xFF]
r = (mem[B] - mem[A]) & 0xFF
mem[B] = r
pc = C if (r == 0 or r >= 0x80) else (pc + 3) & 0xFF
dev.apply(mem)
n += 1
V = torch.stack([L._vec(p, m) for p, m in states]).to(args.device)
worst, frac, y = float("inf"), 0.0, V
for W, b in zip(L.W, L.B):
pre = y @ W.T + b
worst = min(worst, float((pre + 0.5).abs().min()))
frac = max(frac, float((pre - pre.round()).abs().max()))
y = (pre >= 0).float()
print(f"[margin] {len(states)} states sampled along the {n:,} steps of the "
f"run, all {L.info['layers']} layers: minimum distance of a "
f"pre-activation from -1/2 is {worst:.6f}, maximum distance from an "
f"integer is {frac:.3e}")
json.dump({"states": len(states), "steps": n,
"layers": int(L.info["layers"]), "min_margin": worst,
"max_noninteger": frac},
open(runs_path("paper_noise_margin.json"), "w"), indent=1)
return 0 if abs(worst - 0.5) < 1e-9 and frac == 0.0 else 1
def upper_tail(x: float) -> float:
"""P(e > x) for a standard normal e."""
return 0.5 * math.erfc(x / math.sqrt(2.0))
def poisson_cdf(k: int, lam: float) -> float:
"""P(N <= k) for N Poisson with mean lam."""
if k < 0:
return 0.0
if lam <= 0.0:
return 1.0
logs = [i * math.log(lam) - lam - math.lgamma(i + 1) for i in range(k + 1)]
m = max(logs)
return min(1.0, math.exp(m) * sum(math.exp(t - m) for t in logs))
def poisson_interval(k: int, conf: float = 0.95) -> tuple:
"""The exact (Garwood) two-sided interval for the mean of a Poisson count k."""
a = (1.0 - conf) / 2.0
def bisect(f, lo, hi):
for _ in range(200):
mid = (lo + hi) / 2.0
if f(mid):
hi = mid
else:
lo = mid
return (lo + hi) / 2.0
top = 10.0 * (k + 10)
lower = 0.0 if k == 0 else bisect(lambda lam: 1.0 - poisson_cdf(k - 1, lam) >= a, 0.0, top)
upper = bisect(lambda lam: poisson_cdf(k, lam) <= a, 0.0, top)
return lower, upper
def run_profile(args) -> int:
"""The pre-activations along the exact trajectory, and the one-sided bound."""
import torch
from host import M_P, LevEvaluator, describe, read_host
sigma = read_host()
L = LevEvaluator(sigma, device=args.device, dense=False)
M = L.info["size"]
r_H = describe(sigma)
T = args.steps
v = L._vec(0, M_P).unsqueeze(0).to(args.device)
D = Devices(L, [r_H], args.device)
lo, hi = -300, 300
hist = torch.zeros(hi - lo + 1, dtype=torch.float64, device=args.device)
step_hist = torch.zeros(T, hi - lo + 1, dtype=torch.float64, device=args.device)
t0 = time.perf_counter()
for n in range(T):
x = v
for groups, width in zip(L.plan, L.widths):
y = torch.empty(1, width, device=args.device)
for rows, idx, w, b in groups:
pre = (x[:, idx] * w).sum(-1) + b
z = pre.round().long().flatten()
c = torch.bincount(z - lo, minlength=hi - lo + 1).double()
hist += c
step_hist[n] += c
y[:, rows] = (pre >= 0).float()
x = y
v = x
if D.step(v)[0]:
T = n + 1
break
z = torch.arange(lo, hi + 1)
per_step = (hist.cpu() / T)
at = {int(k): float(per_step[k - lo]) for k in range(-3, 3)}
curve = json.load(open(runs_path("paper_noise_curve.json")))
rows = []
steps_hist = step_hist[:T].cpu()
for r in curve["rows"]:
s = r["sigma"]
q = torch.tensor([upper_tail(abs(int(k) + 0.5) / s) for k in z.tolist()],
dtype=torch.float64)
b_t = steps_hist @ q # the bound at every step of the orbit
cum = torch.cat([torch.zeros(1, dtype=torch.float64), b_t.cumsum(0)])
# each copy is exposed from step 1 to its first deviation, or to the
# end of the run; the bound on its expected number of deviations is
# the sum of b_t over those steps
exp_steps = [f if f is not None else r["steps"] for f in r["first_deviation"]]
expected = float(sum(cum[min(e, T)] for e in exp_steps))
exposure = sum(exp_steps)
weighted = expected / exposure if exposure else 0.0
# the rows of a layer read the same exact input and fail independently, so one minus the
# product of their probabilities of holding is the probability that some row fails
bp_t = -torch.expm1(steps_hist @ torch.log1p(-q))
cum_p = torch.cat([torch.zeros(1, dtype=torch.float64), bp_t.cumsum(0)])
expected_p = float(sum(cum_p[min(e, T)] for e in exp_steps))
weighted_p = expected_p / exposure if exposure else 0.0
lo, hi = poisson_interval(r["deviated"])
p = math.erfc(1.0 / (2.0 * math.sqrt(2.0) * s))
rows.append({"sigma": s, "measured": r["empirical_rate_per_step"],
"deviated": r["deviated"], "exposure": exposure,
"rate_interval_95": [lo / exposure, hi / exposure],
"bound_prop": M * p, "bound_profile_mean": float(b_t.mean()),
"bound_profile_exposure": weighted,
"expected_deviations_bound": expected,
"bound_product_exposure": weighted_p,
"expected_deviations_product": expected_p,
"ratio_profile": (weighted / r["empirical_rate_per_step"])
if r["empirical_rate_per_step"] else None,
"ratio_product": (weighted_p / r["empirical_rate_per_step"])
if r["empirical_rate_per_step"] else None})
print(f" s={s:.2f}: measured {r['empirical_rate_per_step']:.3e} "
f"[{lo / exposure:.3e}, {hi / exposure:.3e}] ({r['deviated']} deviations), "
f"profile bound {weighted:.3e}, product form {weighted_p:.3e}, M p {M * p:.3e}",
flush=True)
half = at[-1] + at[0]
print(f"[profile] {T:,} steps of the exact orbit, M = {M:,} rows per step: "
f"on average {at[-1]:,.0f} rows at z = -1 and {at[0]:,.0f} at z = 0, "
f"{half / M:.4f} of all rows at distance 1/2 from the comparator "
f"({time.perf_counter() - t0:.0f} s)")
json.dump({"steps": T, "M": M, "rows_per_step_at": at,
"fraction_at_half": half / M,
"histogram_per_step": {int(k): float(c) for k, c in
zip(z.tolist(), per_step.tolist()) if c},
"rows": rows},
open(runs_path("paper_noise_profile.json"), "w"), indent=1)
return 0
def run_certify(args) -> int:
"""Active-input counts along an orbit."""
import torch
from host import M_P, M_STAR, LevEvaluator, describe, read_host, tau_star
sigma = read_host()
L = LevEvaluator(sigma, device=args.device, dense=False)
M = L.info["size"]
print(f"[certify] M = {M:,} rows in {L.info['layers']} layers", flush=True)
leak = json.load(open(runs_path("paper_noise_leak.json"))) \
if os.path.exists(runs_path("paper_noise_leak.json")) else None
def walk(mem0, tape, cap, label):
v = L._vec(0, mem0).unsqueeze(0).to(args.device)
D = Devices(L, [tape], args.device)
nl = len(L.plan)
a_max = torch.zeros(nl, dtype=torch.long, device=args.device)
k_max = torch.zeros(nl, dtype=torch.long, device=args.device)
pop_max = torch.zeros(nl, dtype=torch.long, device=args.device)
t0 = time.perf_counter()
T = cap
for n in range(cap):
x = v
for li, (groups, width) in enumerate(zip(L.plan, L.widths)):
y = torch.empty(1, width, device=args.device)
pop = x.sum().long()
pop_max[li] = torch.maximum(pop_max[li], pop)
for rows, idx, w, b in groups:
g = x[:, idx]
k1 = (g * (w != 0)).sum(-1).long()
k_max[li] = torch.maximum(k_max[li], k1.max())
a_max[li] = torch.maximum(a_max[li], (pop - k1).max())
y[:, rows] = (((g * w).sum(-1) + b) >= 0).float()
x = y
v = x
if D.step(v)[0]:
T = n + 1
break
if (n + 1) % 100000 == 0:
print(f" {label}: {n + 1:,} steps ({(n + 1) / (time.perf_counter() - t0):.0f}/s)",
flush=True)
A_star, K_star = int(a_max.max()), int(k_max.max())
rec = {"steps": T, "A_star": A_star, "K_star": K_star,
"per_layer_active_zero_max": a_max.tolist(),
"per_layer_active_programmed_max": k_max.tolist(),
"per_layer_popcount_max": pop_max.tolist(),
"gamma_certified": 0.5 / A_star, "epsilon_certified": 0.5 / K_star,
"seconds": time.perf_counter() - t0}
print(f" {label}: {T:,} steps; at most {A_star} active zero-weight inputs and "
f"{K_star} active programmed inputs at any row: gamma < {0.5 / A_star:.3e}, "
f"eps < {0.5 / K_star:.3e} ({rec['seconds']:.0f} s)", flush=True)
return rec
out = {"M": M, "widest_layer": max(L.widths)}
out["selfdesc_10000"] = walk(M_P, describe(sigma), args.steps, "self-describing orbit")
if leak is not None:
A_star, K_star = out["selfdesc_10000"]["A_star"], out["selfdesc_10000"]["K_star"]
rows = []
for r in leak["rows"]:
cert = r["epsilon"] * K_star + r["gamma"] * A_star
rows.append({"epsilon": r["epsilon"], "gamma": r["gamma"], "worst_bound": r["worst_bound"],
"orbit_bound": cert, "certified_by_orbit": cert < 0.5,
"first_deviation": r["first_deviation"]})
out["leak_rows"] = rows
n_cert = sum(r["certified_by_orbit"] for r in rows)
n_exact = sum(r["first_deviation"] is None for r in rows)
print(f" of the {len(rows)} rows of the leak table, Proposition 8.3 certifies "
f"{sum(r['worst_bound'] < 0.5 for r in rows)}, the orbit certificate {n_cert}, "
f"and {n_exact} were exact")
if not args.short:
tau = tau_star(sigma, M_STAR)
out["omega_star"] = walk(M_STAR, tau, 1 << 21, "self-reproducing run")
json.dump(out, open(runs_path("paper_noise_certify.json"), "w"), indent=1)
return 0
def run_masked(args) -> int:
"""The steps in which some row fails, against the product form b'(t), and the fraction of them
that leave the state on the orbit; every noisy copy starts each step from the exact state."""
import torch
from host import M_P, LevEvaluator, describe, read_host
sigma = read_host()
L = LevEvaluator(sigma, device=args.device, dense=True)
M = sum(int(W.shape[0]) for W in L.W)
r_H = describe(sigma)
B, T = args.batch, args.steps
grid = [float(x) for x in args.grid.split(",")]
print(f"[masked] M = {M:,}; {B} noisy copies restarted from the exact state at each of "
f"{T:,} steps", flush=True)
rows = []
for s in grid:
gen = torch.Generator(device=args.device).manual_seed(11 + int(1000 * s))
v = L._vec(0, M_P).unsqueeze(0).to(args.device)
D = Devices(L, [r_H], args.device)
failed = deviated = 0
expected = variance = 0.0
t0 = time.perf_counter()
steps = T
for n in range(T):
y = v.repeat(B + 1, 1)
bad = torch.zeros(B, dtype=torch.bool, device=args.device)
keep = torch.zeros((), dtype=torch.float64, device=args.device)
for W, b in zip(L.W, L.B):
pre = y @ W.T + b
# copy 0 is noise free and reads the orbit's values, so its pre-activations are the
# orbit's, and a row fails with probability Q(|z + 1/2| / s)
q = 0.5 * torch.erfc((pre[0].double() + 0.5).abs() / (s * math.sqrt(2.0)))
keep += torch.log1p(-q).sum()
noise = torch.randn(pre.shape, generator=gen, device=pre.device) * s
noise[0] = 0.0
y = ((pre + noise) >= -0.5).float()
bad |= (y[1:] != y[0]).any(dim=1)
off = (y[1:] != y[0]).any(dim=1)
failed += int(bad.sum())
deviated += int(off.sum())
bp = float(-torch.expm1(keep)) # b'(t): some row of the step fails
expected += B * bp
variance += B * bp * (1.0 - bp)
v = y[0:1]
if D.step(v)[0]:
steps = n + 1
break
dt = time.perf_counter() - t0
n_cs = B * steps
rows.append({"sigma": s, "copies": B, "steps": steps, "copy_steps": n_cs,
"row_failure_steps": failed, "deviations": deviated,
"masked": failed - deviated,
"masked_fraction": (failed - deviated) / failed if failed else None,
"row_failure_rate": failed / n_cs, "deviation_rate": deviated / n_cs,
"product_form_mean": expected / n_cs,
"row_failures_expected": expected,
"row_failures_sd": math.sqrt(variance),
"seconds": dt})
print(f" s={s:.2f}: {failed:,} steps with a failing row against {expected:,.1f} "
f"(sd {math.sqrt(variance):.1f}) from b'; {deviated:,} left the orbit, "
f"{failed - deviated:,} masked"
+ (f" ({(failed - deviated) / failed:.2%})" if failed else "")
+ f" ({dt / 60:.1f} min)", flush=True)
json.dump({"M": M, "grid": grid, "rows": rows},
open(runs_path("paper_noise_masked.json"), "w"), indent=1)
return 0
def run_intervals(args) -> int:
"""Exact Poisson intervals for the measured rates of the host and of the threefold host."""
out = {"confidence": 0.95, "host": [], "amplified_r3": []}
for r in json.load(open(runs_path("paper_noise_curve.json")))["rows"]:
lo, hi = poisson_interval(r["deviated"])
e = r["step_exposure"]
out["host"].append({"sigma": r["sigma"], "deviated": r["deviated"], "exposure": e,
"rate": r["empirical_rate_per_step"], "rate_low": lo / e, "rate_high": hi / e})
for r in json.load(open(runs_path("paper_redundant_restore_r3.json")))["rows"]:
lo, hi = poisson_interval(r["failed"])
e = r["exposure"]
out["amplified_r3"].append({"sigma": r["sigma"], "failed": r["failed"], "exposure": e,
"rate": r["failure_rate_per_step"], "rate_low": lo / e, "rate_high": hi / e})
json.dump(out, open(runs_path("paper_noise_intervals.json"), "w"), indent=1)
print(json.dumps(out, indent=1))
return 0
def main_host() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("mode", choices=["omega", "curve", "leak", "margin", "profile", "certify", "intervals",
"masked"])
ap.add_argument("--short", action="store_true", help="certify: the leak orbit only")
ap.add_argument("--states", type=int, default=800)
ap.add_argument("--device", default="cuda")
ap.add_argument("--batch", type=int, default=256)
ap.add_argument("--grid", default="0.02,0.04,0.06,0.08,0.09,0.10,0.11,0.12,0.15,0.20")
ap.add_argument("--steps", type=int, default=4000)
args = ap.parse_args()
return {"omega": run_omega, "curve": run_curve, "leak": run_leak,
"margin": run_margin, "profile": run_profile,
"certify": run_certify, "intervals": run_intervals, "masked": run_masked}[args.mode](args)
SRC = os.path.join(REPO, "src")
def sha(b: bytes) -> str:
return hashlib.sha256(b).hexdigest()
# ---------------------------------------------------------------------------
# the construction
# ---------------------------------------------------------------------------
class Amplified:
"""N_r = A_r(Lev(N)) for the netlist N of a serialization, in flat form."""
def __init__(self, sigma: bytes, r: int):
assert r >= 1 and r % 2 == 1, "the redundancy must be odd"
n, units, nxt = parse_sigma(sigma)
ln, lunits, lnxt, info = levelize(n, units, nxt)
assert ln == n
self.r, self.n, self.sigma0 = r, n, sigma
self.layers: List[int] = info["layer_sizes"]
self.depth = info["depth"]
self.lev_rows = len(lunits)
self.lev_units = lunits
self.lev_nxt = lnxt
shift = (r - 1) // 2
def pos(k: int, c: int) -> int:
return c * n + k if k < n else r * n + r * (k - n) + c
out: List[Tuple[int, List[Tuple[int, int]]]] = []
for b, ins in lunits:
ins_r = [(pos(k, c), w) for k, w in ins for c in range(r)]
for c in range(r):
out.append((r * b + shift, ins_r))
self.units = out
self.nxt = [r * n + r * (lnxt[i] - n) + c for c in range(r) for i in range(n)]
self.state_bits = r * n
self.edges = sum(len(ins) for _, ins in out)
def sigma(self) -> bytes:
bias, fanin, pred = [], [], []
for b, ins in self.units:
bias.append(b)
fanin.append(len(ins))
for k, w in ins:
pred.append(w * (k + 1))
lay = dict(STATE_LAYOUT)
lay["replicas"] = self.r
lay["copy_stride"] = self.n
return encode(8, bias, fanin, pred, self.nxt, f"subleq8r{self.r}", lay, IO_CELLS)
def write_flat(self, path: str) -> None:
write_flat(path, self.state_bits, self.units, self.nxt)
# -- the layered plan for the evaluator on the accelerator ----------------
def plan(self, device: str, pad: bool):
"""Per layer, the rows, input positions, weights and biases of the amplified layer."""
import torch
r, n = self.r, self.n
base = [n]
for L in self.layers:
base.append(base[-1] + L)
plan = []
j = 0
for ell, L in enumerate(self.layers, start=1):
rows_of = []
for m in range(L):
b, ins = self.lev_units[j]
j += 1
if ell == 1:
idx = [c * n + k for k, w in ins for c in range(r)]
else:
idx = [r * (k - base[ell - 2]) + c for k, w in ins for c in range(r)]
w = [wt for k, wt in ins for c in range(r)]
for c in range(r):
rows_of.append((m * r + c, idx, w, r * b + (r - 1) // 2))
groups = []
if pad:
K = max(len(x[1]) for x in rows_of)
idx_t = torch.zeros(len(rows_of), K, dtype=torch.long)
w_t = torch.zeros(len(rows_of), K)
for q, (row, idx, w, b) in enumerate(rows_of):
idx_t[q, :len(idx)] = torch.tensor(idx)
w_t[q, :len(w)] = torch.tensor(w, dtype=torch.float32)
rows_t = torch.tensor([x[0] for x in rows_of])
b_t = torch.tensor([x[3] for x in rows_of], dtype=torch.float32)
groups.append((rows_t.to(device), idx_t.to(device), w_t.to(device), b_t.to(device)))
else:
by_k: Dict[int, list] = {}
for x in rows_of:
by_k.setdefault(len(x[1]), []).append(x)
for K in sorted(by_k):
g = by_k[K]
rows_t = torch.tensor([x[0] for x in g])
idx_t = torch.tensor([x[1] for x in g], dtype=torch.long)
w_t = torch.tensor([x[2] for x in g], dtype=torch.float32)
b_t = torch.tensor([x[3] for x in g], dtype=torch.float32)
groups.append((rows_t.to(device), idx_t.to(device), w_t.to(device), b_t.to(device)))
plan.append((r * L, groups))
# the final layer holds copy c of state bit i at i r + c; the state
# holds it at c n + i
perm = torch.tensor([i * r + c for c in range(r) for i in range(n)]).to(device)
return plan, perm
class Evaluator:
"""N_r on the accelerator, with read noise and the device on the copies."""
def __init__(self, amp: Amplified, device: str = "cuda", pad: bool = True,
graph: bool = False):
import torch
self.torch = torch
self.amp, self.device = amp, device
self.r, self.n = amp.r, amp.n
self.plan, self.perm = amp.plan(device, pad)
self.M = sum(L for L, _ in self.plan)
self.N = amp.state_bits
cells = (IO_CELLS["c_wr"], IO_CELLS["c_eot"], IO_CELLS["c_rw"],
IO_CELLS["c_rd"], IO_CELLS["r_in"], IO_CELLS["r_out"])
self.cells = cells
# bit t of cell j of copy c
self.cell_idx = torch.tensor([[c * self.n + 9 + 8 * j + t for j in cells for t in range(8)]
for c in range(self.r)], device=device)
self.pow2 = torch.tensor([1 << (7 - t) for t in range(8)], device=device)
self.shift = torch.tensor([7 - t for t in range(8)])
self.graph = None
self.gen = None
if graph:
self.capture()
# -- one application of the map, over a batch --------------------------
def _forward(self, v, s: float = 0.0, gen=None):
torch = self.torch
x = v
for width, groups in self.plan:
y = torch.empty(x.shape[0], width, device=x.device)
for rows, idx, w, b in groups:
pre = (x[:, idx] * w).sum(-1) + b
if s > 0:
pre = pre + torch.randn(pre.shape, generator=gen, device=pre.device) * s
y[:, rows] = (pre >= -0.5).float()
x = y
return x[:, self.perm]
def step(self, v):
if self.graph is not None and v.shape[0] == 1 and self.gs == 0.0:
return self._replay(v)
return self._forward(v)
def step_noisy(self, v, s: float, gen):
if self.graph is not None and v.shape[0] == 1 and self.gs == s and gen is self.gen:
return self._replay(v)
return self._forward(v, s, gen)
def capture(self, s: float = 0.0, seed: int = 0):
"""Capture one application, with noise of deviation s, as a CUDA graph."""
torch = self.torch
self.gs = s
self.gin = torch.zeros(1, self.N, device=self.device)
gen = None
if s > 0:
gen = torch.Generator(device=self.device).manual_seed(seed)
self.gen = gen
for _ in range(3):
self._forward(self.gin, s, gen)
torch.cuda.synchronize()
self.graph = torch.cuda.CUDAGraph()
with torch.cuda.graph(self.graph):
self.gout = self._forward(self.gin, s, gen)
torch.cuda.synchronize()
if s == 0.0:
probe = (torch.rand(1, self.N, device=self.device) < 0.5).float()
self.gin.copy_(probe)
self.graph.replay()
torch.cuda.synchronize()
assert bool((self.gout == self._forward(probe)).all()), \
"the captured graph differs from the eager step"
return self
def _replay(self, v):
if v.data_ptr() != self.gin.data_ptr():
self.gin.copy_(v)
self.graph.replay()
self.gin.copy_(self.gout)
return self.gin
# -- states --------------------------------------------------------------
def vec(self, pc: int, mem: List[int], copies: int = 1):
torch = self.torch
v0 = torch.zeros(self.n)
for k in range(8):
v0[k] = (pc >> (7 - k)) & 1
for j in range(256):
for k in range(8):
v0[9 + j * 8 + k] = (mem[j] >> (7 - k)) & 1
v = v0.repeat(self.r).unsqueeze(0).to(self.device)
return v.repeat(copies, 1)
def device_step(self, v, tapes: List[Tape], shadow: List[List[int]]):
"""The device on every copy: cells read by majority, every copy written."""
torch = self.torch
B = v.shape[0]
bits = v[:, self.cell_idx] # (B, r, 48)
maj = (bits.sum(1) * 2 > self.r).long() # (B, 48)
vals = (maj.reshape(B, len(self.cells), 8) * self.pow2).sum(-1).cpu()
halts = (v[:, [c * self.n + 8 for c in range(self.r)]].sum(1) * 2 > self.r).cpu().tolist()
new = torch.empty(B, len(self.cells), dtype=torch.long)
for i in range(B):
for k, j in enumerate(self.cells):
shadow[i][j] = int(vals[i, k])
tapes[i].apply(shadow[i])
for k, j in enumerate(self.cells):
new[i, k] = shadow[i][j]
nb = ((new.unsqueeze(-1) >> self.shift) & 1).reshape(B, 1, -1).float().to(self.device)
v[:, self.cell_idx] = nb.expand(B, self.r, -1)
return halts
# ---------------------------------------------------------------------------
def build_amplified(r: int) -> Amplified:
return Amplified(read_host(), r)
def compile_c(work: str, cc: str = "gcc") -> str:
"""The evaluator in C, the levels of a large netlist evaluated in parallel."""
exe = os.path.join(work, "netlist_eval")
subprocess.run([cc, "-O3", "-march=native", "-fopenmp", "-o", exe,
os.path.join(SRC, "netlist_eval.c")], check=True)
os.environ.setdefault("OMP_WAIT_POLICY", "ACTIVE")
os.environ.setdefault("OMP_PROC_BIND", "true")
os.environ.setdefault("OMP_NUM_THREADS", str(max(1, min(16, (os.cpu_count() or 2) - 4))))
return exe
def cmd_build(args) -> int:
t0 = time.perf_counter()
amp = build_amplified(args.r)
s = amp.sigma()
t_build = time.perf_counter() - t0
rec = {"r": args.r, "state_bits": amp.state_bits, "units": len(amp.units),
"edges": amp.edges, "depth": amp.depth, "lev_rows": amp.lev_rows,
"layer_widths": [args.r * L for L in amp.layers],
"sigma_bytes": len(s), "sigma_sha256": sha(s),
"bias_min": min(b for b, _ in amp.units), "bias_max": max(b for b, _ in amp.units),
"max_fanin": max(len(i) for _, i in amp.units), "build_seconds": t_build}
print(f"[build] N_{args.r}: {amp.state_bits:,} state bits, {len(amp.units):,} units, "
f"{amp.edges:,} edges, depth {amp.depth}; sigma {len(s):,} bytes, "
f"sha {sha(s)[:16]} ({t_build:.1f} s)", flush=True)
# the serialization denotes the netlist it was written from
n2, u2, x2 = parse_sigma(s)
rec["sigma_roundtrip"] = (n2 == amp.state_bits and u2 == amp.units and x2 == amp.nxt)
print(f" parses back to the same netlist: {rec['sigma_roundtrip']}")
# one step against SUBLEQ on the evaluator in C, every copy checked
work = tempfile.mkdtemp(prefix="redundant_")
exe = compile_c(work, args.cc)
flat = os.path.join(work, "net.bin")
amp.write_flat(flat)
p = subprocess.run([exe, "rand", flat, str(args.states), "1"], capture_output=True, text=True)
rec["c_random"] = p.stdout.strip()
rec["c_random_ok"] = p.returncode == 0 and " 0 disagree" in p.stdout
print(f" {p.stderr.strip()}")
print(f" {p.stdout.strip()}")
# the margins: every pre-activation on a replicated state is r z + (r-1)/2
import torch
device = "cuda" if torch.cuda.is_available() else "cpu"
E = Evaluator(amp, device=device, pad=True)
gen = torch.Generator().manual_seed(11)
worst = float("inf")
for _ in range(4):
base = (torch.rand(8, amp.n, generator=gen) < 0.5).float()
x = base.repeat(1, args.r).to(device)
for width, groups in E.plan:
y = torch.empty(x.shape[0], width, device=device)
for rows, idx, w, b in groups:
pre = (x[:, idx] * w).sum(-1) + b
worst = min(worst, float((pre + 0.5).abs().min()))
y[:, rows] = (pre >= 0).float()
x = y
rec["min_margin_random_states"] = worst
print(f" minimum distance of a pre-activation from -1/2 on 32 replicated random "
f"states: {worst}")
# the evaluator on the accelerator agrees with the evaluator in C on a prefix
tau = tau_star(read_host(), M_STAR)
v = E.vec(0, M_STAR)
tapes, shadow = [Tape(tau)], [[0] * 256]
for _ in range(300):
v = E.step(v)
E.device_step(v, tapes, shadow)
st = v[0].cpu().long().tolist()
open(os.path.join(work, "mstar"), "wb").write(bytes(M_STAR))
open(os.path.join(work, "tau"), "wb").write(tau)
p = subprocess.run([exe, "run", flat, os.path.join(work, "mstar"), os.path.join(work, "tau"),
os.path.join(work, "out"), "net", "300"], capture_output=True, text=True)
# replay the C state through the same 300 steps by the reference, then compare the
# emitted prefix and the memory read by majority
mem = list(M_STAR); pc = 0; dev = Tape(tau)
for _ in range(300):
a, b, c = mem[pc], mem[(pc + 1) & 255], mem[(pc + 2) & 255]
rr = (mem[b] - mem[a]) & 255
mem[b] = rr
pc = c if (rr == 0 or rr >= 128) else (pc + 3) & 255
dev.apply(mem)
got_mem = [sum(st[c * amp.n + 9 + 8 * j + t] << (7 - t) for t in range(8))
for j in range(256) for c in (0,)]
all_copies_equal = all(st[c * amp.n + i] == st[i] for c in range(args.r) for i in range(amp.n))
rec["accelerator_prefix_ok"] = (got_mem == mem and all_copies_equal
and tapes[0].out == dev.out
and open(os.path.join(work, "out"), "rb").read() == bytes(dev.out))
print(f" 300 steps of Omega* on the accelerator and in C agree with the reference, "
f"all copies equal: {rec['accelerator_prefix_ok']}")
rec["ok"] = bool(rec["sigma_roundtrip"] and rec["c_random_ok"]
and abs(worst - args.r / 2) < 1e-9 and rec["accelerator_prefix_ok"])
json.dump(rec, open(runs_path(f"paper_redundant_r{args.r}.json"), "w"), indent=1)
print("REDUNDANT HOST:", "PASS" if rec["ok"] else "FAIL")
return 0 if rec["ok"] else 1
def census(r: bytes) -> Dict[str, int]:
"""L, R, f_lit, f_rep of a recipe."""
L = R = fl = fr = 0
i = 0
while True:
t = r[i]
if t == 0:
break
if 1 <= t <= 127:
L += 1; fl += t; i += 1 + t
else:
R += 1; fr += 256 - t; i += 2
return {"L": L, "R": R, "f_lit": fl, "f_rep": fr}
def closed_form(c: Dict[str, int], tape_len: int) -> Dict[str, int]:
a = 5 * c["f_lit"] + 4 * c["f_rep"] + 7 * c["L"] + 9 * c["R"] + 7
return {"A": a, "R": 1, "B": 6 * tape_len + 2, "total": a + 1 + 6 * tape_len + 2}
def cmd_selfrep(args) -> int:
"""Omega*_r on the evaluator in C in lockstep with the reference, to completion."""
t0 = time.perf_counter()
amp = build_amplified(args.r)
s_r = amp.sigma()
tau = tau_star(s_r, M_STAR)
target = ser(s_r, bytes(M_STAR), tau)
cen = census(tau)
cf = closed_form(cen, len(tau))
print(f"[selfrep] N_{args.r}: sigma {len(s_r):,} bytes, tau*_r {len(tau):,} bytes, "
f"ser {len(target):,} bytes; closed form {cf['total']:,} steps", flush=True)
work = tempfile.mkdtemp(prefix="redundant_")
exe = compile_c(work, args.cc)
flat = os.path.join(work, "net.bin")
amp.write_flat(flat)
open(os.path.join(work, "mstar"), "wb").write(bytes(M_STAR))
open(os.path.join(work, "tau"), "wb").write(tau)
t1 = time.perf_counter()
p = subprocess.run([exe, "run", flat, os.path.join(work, "mstar"), os.path.join(work, "tau"),
os.path.join(work, "out"), args.cmode], capture_output=True, text=True)
dt = time.perf_counter() - t1
out = open(os.path.join(work, "out"), "rb").read()
exact = out == target
import re
m = re.search(r"after (\d+) steps", p.stdout)
steps = int(m.group(1)) if m else None
sg, mb, tb = inst(out) if exact else (None, None, None)
rec = {"r": args.r, "sigma_bytes": len(s_r), "sigma_sha256": sha(s_r),
"tau_bytes": len(tau), "ser_bytes": len(target), "ser_sha256": sha(target),
"census": cen, "closed_form": cf, "mode": args.cmode,
"c_result": p.stdout.strip(), "steps": steps, "seconds": dt,
"steps_per_second": steps / dt if steps and dt else None,
"out_bytes": len(out), "out_sha256": sha(out), "equals_ser": exact,
"closed_form_agrees": steps == cf["total"],
"inst_recovers_instance": bool(exact and sg == s_r and mb == bytes(M_STAR) and tb == tau),
"units": len(amp.units), "edges": amp.edges, "state_bits": amp.state_bits}
rec["ok"] = bool(exact and rec["closed_form_agrees"] and rec["inst_recovers_instance"]
and p.returncode == 0)
print(f" {p.stderr.strip()}")
print(f" {p.stdout.strip()} ({dt / 60:.1f} min, {rec['steps_per_second'] or 0:,.0f} steps/s)")
print(f" Out = ser(Omega*_r): {exact}; closed form agrees: {rec['closed_form_agrees']}; "
f"inst recovers the instance: {rec['inst_recovers_instance']}; sha {sha(out)[:16]}")
rec["total_seconds"] = time.perf_counter() - t0
json.dump(rec, open(runs_path(f"paper_redundant_selfrep_r{args.r}.json"), "w"), indent=1)
print("REDUNDANT SELF-REPRODUCTION:", "PASS" if rec["ok"] else "FAIL")
return 0 if rec["ok"] else 1
def tail_bound(s: float, r: int, M: int, T: int) -> float:
"""T M erfc(r / (2 sqrt 2 s)): the bound of the fault-tolerance theorem."""
return T * M * math.erfc(r / (2.0 * math.sqrt(2.0) * s))
def cmd_omega(args) -> int:
"""Omega* run on N_r under read noise on every pre-activation, to completion."""
import torch
amp = build_amplified(args.r)
sigma = read_host()
tau = tau_star(sigma, M_STAR)
target = ser(sigma, bytes(M_STAR), tau)
E = Evaluator(amp, device=args.device, pad=True)
M = E.M
print(f"[omega] N_{args.r}: {amp.depth} layers, M = {M:,}, target {len(target):,} bytes",
flush=True)
grid = [float(x) for x in args.grid.split(",")]
rows = []
for s in grid:
seed = 20260929 + int(1000 * s)
try:
E.capture(s, seed)
gen = E.gen
except Exception as e: # noqa: BLE001
print(f" (graph capture unavailable: {e}; running eagerly)", flush=True)
E.graph = None
gen = torch.Generator(device=args.device).manual_seed(seed)
v = E.vec(0, M_STAR)
tapes, shadow = [Tape(tau)], [[0] * 256]
steps, wrong, halted = 0, None, False
budget = args.budget
t0 = time.perf_counter()
while steps < budget:
v = E.step_noisy(v, s, gen)
steps += 1
before = len(tapes[0].out)
halted = E.device_step(v, tapes, shadow)[0]
out = tapes[0].out
if len(out) > before:
k = len(out) - 1
if k >= len(target) or out[k] != target[k]:
wrong = k
break
if halted:
break
if steps % 200000 == 0:
print(f" s={s:.2f}: {steps:,} steps, {len(out):,} bytes "
f"({steps / (time.perf_counter() - t0):.0f}/s)", flush=True)
dt = time.perf_counter() - t0
emitted = len(tapes[0].out)
complete = emitted == len(target) and wrong is None and halted
outcome = ("emitted the whole instance" if complete else
f"a wrong byte after {wrong} correct" if wrong is not None
else "the halt bit was set" if halted else "the budget was exhausted")
rows.append({"sigma": s, "steps": steps, "bytes": emitted, "complete": complete,
"first_wrong_byte": wrong, "halted": halted, "seconds": dt,
"bound": tail_bound(s, args.r, M, max(steps, 1))})
print(f" s={s:.2f}: {steps:,} steps, {emitted:,} bytes, {outcome} "
f"({dt / 60:.1f} min); bound {rows[-1]['bound']:.3e}", flush=True)
json.dump({"r": args.r, "M": M, "target_bytes": len(target), "rows": rows},
open(runs_path(f"paper_redundant_omega_r{args.r}.json"), "w"), indent=1)
return 0
def upper_tail_amp(x: float) -> float:
return 0.5 * math.erfc(x / math.sqrt(2.0))
def cmd_curve(args) -> int:
"""The rate at which noisy copies of N_r leave the orbit, against the profile and first-order bounds."""
import torch
amp = build_amplified(args.r)
sigma = read_host()
r_H = describe(sigma)
E = Evaluator(amp, device=args.device, pad=False)
M = E.M
B = args.batch
print(f"[curve] N_{args.r}: M = {M:,}; {B} noisy copies per level, {args.steps:,} steps each",
flush=True)
prof = None
pp = runs_path("paper_noise_profile.json")
if os.path.exists(pp):
prof = json.load(open(pp))["histogram_per_step"]
grid = [float(x) for x in args.grid.split(",")]
rows = []
for s in grid:
cap = args.steps if s >= 0.24 else min(args.steps, 3000)
gen = torch.Generator(device=args.device).manual_seed(7 + int(1000 * s))
v = E.vec(0, M_P, copies=B + 1)
tapes = [Tape(r_H) for _ in range(B + 1)]
shadow = [[0] * 256 for _ in range(B + 1)]
first = [None] * B
live = cap
t0 = time.perf_counter()
for n in range(cap):
x = v
for width, groups in E.plan:
y = torch.empty(x.shape[0], width, device=x.device)
for rws, idx, w, b in groups:
pre = (x[:, idx] * w).sum(-1) + b
noise = torch.randn(pre.shape, generator=gen, device=pre.device) * s
noise[0] = 0.0
y[:, rws] = ((pre + noise) >= -0.5).float()
x = y
v = x[:, E.perm]
diff = (v[1:] != v[0]).any(dim=1).cpu().tolist()
for i, d in enumerate(diff):
if d and first[i] is None:
first[i] = n + 1
if all(f is not None for f in first):
live = n + 1
break
E.device_step(v, tapes, shadow)
dt = time.perf_counter() - t0
deviated = [f for f in first if f is not None]
exposure = sum(f if f is not None else live for f in first)
rate = len(deviated) / exposure if exposure else 0.0
bound_tail = M * math.erfc(args.r / (2.0 * math.sqrt(2.0) * s))
bound_profile = None
if prof is not None:
bound_profile = args.r * sum(c * upper_tail_amp(args.r * abs(int(z) + 0.5) / s)
for z, c in prof.items())
rows.append({"sigma": s, "copies": B, "steps": live, "deviated": len(deviated),
"median_first_deviation":
sorted(deviated)[len(deviated) // 2] if deviated else None,
"step_exposure": exposure, "empirical_rate_per_step": rate,
"bound_tail_per_step": bound_tail,
"bound_profile_per_step": bound_profile,
"first_deviation": first, "seconds": dt})
print(f" s={s:.2f}: {len(deviated)}/{B} deviated within {live:,} steps; "
f"empirical {rate:.3e} per step, profile bound "
f"{bound_profile if bound_profile is None else format(bound_profile, '.3e')}, "
f"M erfc {bound_tail:.3e} ({dt / 60:.1f} min)", flush=True)
json.dump({"r": args.r, "M": M, "grid": grid, "rows": rows},
open(runs_path(f"paper_redundant_curve_r{args.r}.json"), "w"), indent=1)
return 0
def cmd_restore(args) -> int:
"""One exact and B noisy copies of N_r stepped together, each read by majority."""
import torch
amp = build_amplified(args.r)
sigma = read_host()
r_H = describe(sigma)
E = Evaluator(amp, device=args.device, pad=False)
n, r = amp.n, args.r
B = args.batch
grid = [float(x) for x in args.grid.split(",")]
rows = []
print(f"[restore] N_{r}: {B} noisy copies, up to {args.steps:,} steps per level", flush=True)
for s in grid:
gen = torch.Generator(device=args.device).manual_seed(31 + int(1000 * s))
v = E.vec(0, M_P, copies=B + 1)
tapes = [Tape(r_H) for _ in range(B + 1)]
shadow = [[0] * 256 for _ in range(B + 1)]
first = [None] * B
wrong_copy_steps = 0
wrong_copies = 0
worst = 0
live = torch.ones(B, dtype=torch.bool)
t0 = time.perf_counter()
ran = args.steps
for t in range(args.steps):
x = v
for width, groups in E.plan:
y = torch.empty(x.shape[0], width, device=x.device)
for rws, idx, w, b in groups:
pre = (x[:, idx] * w).sum(-1) + b
noise = torch.randn(pre.shape, generator=gen, device=pre.device) * s
noise[0] = 0.0
y[:, rws] = ((pre + noise) >= -0.5).float()
x = y
v = x[:, E.perm]
ex = v[0].reshape(r, n)
wrong = (v[1:].reshape(B, r, n) != ex.unsqueeze(0))
k = wrong.sum(dim=(1, 2)).cpu()
maj = (v[1:].reshape(B, r, n).sum(1) * 2 > r)
bad = (maj != (ex[0] > 0.5).unsqueeze(0)).any(1).cpu()
for i in range(B):
if live[i]:
if int(k[i]):
wrong_copy_steps += 1
wrong_copies += int(k[i])
worst = max(worst, int(k[i]))
if bool(bad[i]):
first[i] = t + 1
live[i] = False
if not bool(live.any()):
ran = t + 1
break
E.device_step(v, tapes, shadow)
dt = time.perf_counter() - t0
failed = [f for f in first if f is not None]
exposure = sum(f if f is not None else ran for f in first)
rows.append({"sigma": s, "copies": B, "steps": ran, "failed": len(failed),
"median_first_failure": sorted(failed)[len(failed) // 2] if failed else None,
"exposure": exposure, "failure_rate_per_step": len(failed) / exposure,
"steps_with_wrong_copies": wrong_copy_steps, "wrong_copies": wrong_copies,
"max_wrong_copies_in_a_step": worst, "first_failure": first, "seconds": dt})
print(f" s={s:.2f}: {len(failed)}/{B} majority-read failures within {ran:,} steps, "
f"exposure {exposure:,}, rate {len(failed) / exposure:.3e} per step; "
f"{wrong_copies:,} wrong copies in {wrong_copy_steps:,} copy-steps before the "
f"failures ({dt / 60:.1f} min)", flush=True)
json.dump({"r": r, "copies": B, "cap": args.steps, "rows": rows},
open(runs_path(f"paper_redundant_restore_r{r}.json"), "w"), indent=1)
return 0
# ---------------------------------------------------------------------------
# the restoration bound
# ---------------------------------------------------------------------------
def write_bound_input(amp: "Amplified", path: str, mem: List[int], tape: bytes) -> None:
"""Lev(N_host) layer by layer as CSR, with the device cells, memory and tape, for restore_bound.c."""
import struct
from host import C_WR, C_EOT, C_RW, C_RD, R_IN, R_OUT
n = amp.n
base = [n]
for L in amp.layers:
base.append(base[-1] + L)
assert amp.layers[-1] == n and all(amp.lev_nxt[i] == base[-2] + i for i in range(n)), \
"the last layer of Lev(N_host) is the state, in order"
with open(path, "wb") as f:
f.write(struct.pack("<ii", n, len(amp.layers)))
j0 = 0
for ell, L in enumerate(amp.layers, start=1):
ip, ix, w, bias = [0], [], [], []
for m in range(L):
b, ins = amp.lev_units[j0 + m]
for k, wt in sorted(ins):
ix.append(k if ell == 1 else k - base[ell - 2])
w.append(wt)
ip.append(len(ix))
bias.append(b)
j0 += L
cols = n if ell == 1 else amp.layers[ell - 2]
f.write(struct.pack("<iii", L, cols, len(ix)))
for arr in (ip, ix, w, bias):
f.write(struct.pack(f"<{len(arr)}i", *arr))
dev = sorted(9 + 8 * c + t for c in (C_WR, C_EOT, C_RW, C_RD, R_IN, R_OUT) for t in range(8))
f.write(struct.pack("<i", len(dev)))
f.write(struct.pack(f"<{len(dev)}i", *dev))
f.write(bytes(mem))
f.write(struct.pack("<i", len(tape)))
f.write(bytes(tape))
def bound_series(exe: str, inp: str, s: float, total: int, chunks: int, workers: int,
work: str, warm: int = 20):
"""b(t) for t = 0 .. total-1, or to the halt, in chunks run in parallel."""
import numpy as np
from concurrent.futures import ThreadPoolExecutor
edges = np.linspace(0, total, chunks + 1).astype(int)
def one(k):
a, b = int(edges[k]), int(edges[k + 1])
out = os.path.join(work, f"b_{s:.3f}_{k}.bin")
p = subprocess.run([exe, inp, repr(s), str(a), str(b - a), str(warm), "1e-20", out],
capture_output=True, text=True)
assert p.returncode == 0, p.stderr
vals = np.fromfile(out)
os.remove(out)
return vals, "halted=1" in p.stdout
with ThreadPoolExecutor(workers) as ex:
res = list(ex.map(one, range(chunks)))
series, halted = [], False
for vals, h in res:
series.append(vals)
if h:
halted = True
break
return np.concatenate(series), halted
def cmd_bound(args) -> int:
"""The restoration bound of Proposition 8.14 along the exact orbit, by restore_bound.c."""
import numpy as np
amp = build_amplified(3)
sigma = read_host()
work = tempfile.mkdtemp()
exe = os.path.join(work, "restore_bound")
subprocess.run([args.cc, "-O3", "-march=native", "-o", exe, os.path.join(SRC, "restore_bound.c"), "-lm"],
check=True)
desc_in, repr_in = os.path.join(work, "describing.bin"), os.path.join(work, "reproducing.bin")
write_bound_input(amp, desc_in, M_P, describe(sigma))
write_bound_input(amp, repr_in, M_STAR, tau_star(sigma, M_STAR))
restore = {}
rp = runs_path("paper_redundant_restore_r3.json")
if os.path.exists(rp):
for row in json.load(open(rp))["rows"]:
restore[round(row["sigma"], 3)] = row
workers = max(1, min(args.workers, os.cpu_count() or 1))
out = {"r": 3, "eps_rel": 1e-20, "warm": 20, "steps": args.steps, "rows": [], "whole_run": []}
for s in [float(x) for x in args.grid.split(",")]:
t0 = time.perf_counter()
b, _ = bound_series(exe, desc_in, s, args.steps, args.chunks, workers, work)
cb = np.concatenate([[0.0], np.cumsum(b)])
row = {"sigma": s, "mean_per_step": float(cb[-1] / len(b)), "copy_fails_within": float(cb[-1]),
"max_per_step": float(b.max()),
"first_order_per_step": 3 * sum(amp.layers) * math.erfc(3 / (2 * math.sqrt(2) * s))}
m = restore.get(round(s, 3))
if m:
ran = [f if f is not None else m["steps"] for f in m["first_failure"]]
row["weighted_per_step"] = float(sum(cb[e] for e in ran) / sum(ran))
row["measured_per_step"] = m["failure_rate_per_step"]
row["bound_over_measured"] = row["weighted_per_step"] / m["failure_rate_per_step"] \
if m["failure_rate_per_step"] > 0 else None
row["seconds"] = time.perf_counter() - t0
out["rows"].append(row)
print(f" s={s:.2f}: {row.get('weighted_per_step', row['mean_per_step']):.3e} per step"
+ (f", measured {row['measured_per_step']:.3e}" if m else "")
+ f", first-order {row['first_order_per_step']:.3e} ({row['seconds']:.0f} s)", flush=True)
json.dump(out, open(runs_path("paper_redundant_bound_r3.json"), "w"), indent=1)
for s in [float(x) for x in args.whole.split(",") if x]:
t0 = time.perf_counter()
b, halted = bound_series(exe, repr_in, s, 1_664_939, args.whole_chunks, workers, work)
row = {"sigma": s, "steps": len(b), "halted": halted, "sum": float(b.sum()),
"first_order": len(b) * 3 * sum(amp.layers) * math.erfc(3 / (2 * math.sqrt(2) * s)),
"seconds": time.perf_counter() - t0}
out["whole_run"].append(row)
print(f" whole run, s={s:.2f}: {row['sum']:.3e} over {row['steps']:,} steps, first-order "
f"{row['first_order']:.3e} ({row['seconds'] / 60:.1f} min)", flush=True)
json.dump(out, open(runs_path("paper_redundant_bound_r3.json"), "w"), indent=1)
return 0
def cmd_layers(args) -> int:
"""The profile bound of N_r split by layer."""
import torch
from host import M_P, LevEvaluator
r = args.r
sigma = read_host()
L = LevEvaluator(sigma, device=args.device, dense=False)
v = L._vec(0, M_P).unsqueeze(0).to(args.device)
D = Devices(L, [describe(sigma)], args.device)
lo, hi = -300, 300
nl = len(L.plan)
H = torch.zeros(nl, hi - lo + 1, dtype=torch.float64, device=args.device)
T = args.steps
for n in range(args.steps):
x = v
for li, (groups, width) in enumerate(zip(L.plan, L.widths)):
y = torch.empty(1, width, device=args.device)
for rows, idx, w, b in groups:
pre = (x[:, idx] * w).sum(-1) + b
H[li] += torch.bincount(pre.round().long().flatten() - lo,
minlength=hi - lo + 1).double()
y[:, rows] = (pre >= 0).float()
x = y
v = x
if D.step(v)[0]:
T = n + 1
break
H = (H / T).cpu()
zs = list(range(lo, hi + 1))
measured = {}
cp = runs_path(f"paper_redundant_curve_r{r}.json")
if os.path.exists(cp):
for row in json.load(open(cp))["rows"]:
measured[round(row["sigma"], 3)] = (row["empirical_rate_per_step"], row["deviated"])
out = {"r": r, "steps": T,
"per_layer_rows_at_half": [float(H[l][-1 - lo] + H[l][-lo]) for l in range(nl)],
"last_layer_hist": {z: float(H[nl - 1][z - lo]) for z in zs if H[nl - 1][z - lo] > 0},
"rows": []}
for s in [float(x) for x in args.grid.split(",")]:
q = torch.tensor([upper_tail_amp(r * abs(z + 0.5) / s) for z in zs], dtype=torch.float64)
per_layer = [r * float(H[l] @ q) for l in range(nl)]
m = measured.get(round(s, 3))
out["rows"].append({"sigma": s, "measured": m[0] if m else None,
"deviated": m[1] if m else None, "full": sum(per_layer),
"last_layer": per_layer[-1], "per_layer": per_layer})
print(f" s={s:.2f}: last layer {per_layer[-1]:.3e}, all layers {sum(per_layer):.3e}"
+ (f", measured {m[0]:.3e} ({m[1]} deviations)" if m else ""), flush=True)
json.dump(out, open(runs_path(f"paper_redundant_layers_r{r}.json"), "w"), indent=1)
return 0
def main_amplified() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("mode", choices=["build", "selfrep", "omega", "curve", "restore", "layers", "bound"])
ap.add_argument("--r", type=int, default=3)
ap.add_argument("--cc", default="gcc")
ap.add_argument("--device", default="cuda")
ap.add_argument("--states", type=int, default=1 << 16)
ap.add_argument("--batch", type=int, default=256)
ap.add_argument("--steps", type=int, default=None)
ap.add_argument("--budget", type=int, default=1 << 21)
ap.add_argument("--cmode", default="lock",
help="for selfrep: lock (compared with the reference every step) or net")
ap.add_argument("--grid", default=None)
ap.add_argument("--whole", default="0.20,0.22,0.25,0.27",
help="for bound: the noise levels of the sums over the self-reproducing run")
ap.add_argument("--chunks", type=int, default=10, help="for bound: chunks of the 20,000 steps")
ap.add_argument("--whole-chunks", type=int, default=36, help="for bound: chunks of the whole run")
ap.add_argument("--workers", type=int, default=6, help="for bound: chunks run at once")
args = ap.parse_args()
if args.steps is None:
args.steps = 20000 if args.mode == "bound" else 4000
if args.grid is None:
args.grid = {"omega": "0.10,0.15,0.20,0.25",
"curve": "0.06,0.12,0.18,0.24,0.27,0.30,0.33,0.36,0.45,0.60",
"restore": "0.27,0.30,0.33,0.36",
"layers": "0.06,0.12,0.18,0.24,0.27,0.30,0.33,0.36,0.45,0.60",
"bound": "0.10,0.15,0.20,0.22,0.25,0.27,0.30,0.33,0.36"}.get(args.mode, "")
return {"build": cmd_build, "selfrep": cmd_selfrep, "omega": cmd_omega,
"curve": cmd_curve, "restore": cmd_restore, "layers": cmd_layers,
"bound": cmd_bound}[args.mode](args)
COMMANDS = {'host': main_host, 'amplified': main_amplified}
if __name__ == "__main__":
if len(sys.argv) < 2 or sys.argv[1] not in COMMANDS:
sys.exit('commands: ' + ', '.join(COMMANDS))
cmd = sys.argv.pop(1)
sys.argv[0] += ' ' + cmd
sys.exit(COMMANDS[cmd]())