def build_circuit(b, p=P_MOD, n=N):
"""Emit the point addition |P> -> |P+Q>; Q per shot on the
Bit-kind inputs. Returns (x1, y1, ox, oy).
"""
x1, y1, ox, oy = LC.declare_point_io(b, n)
# per-shot cbspecs (free classical networks of the addend bits)
S_x2, S_y2, S_3x2 = LC.point_cbspecs(b, ox, oy, p, n)
A = b.qubits(n)
mm = E.MulMachine(b)
singles, ma = mm.singles, mm.ma
b.claim("io/x1", x1)
b.claim("io/y1", y1)
b.claim("A", A)
mm.claim()
inv = E.HmrDivstepInv(b, x1)
Subtract Q 16,320 Toffolis · peak 775 qubits
with b.phase("s1-const-subs"):
LC.const_modp(b, x1, S_x2, singles["flag"], y1, singles["clean3"], p, n, inv=True)
LC.const_modp(b, y1, S_y2, singles["flag"], x1, singles["clean3"], p, n, inv=True)
# ================= s2-idiv: y1 <- y1 / x1 ==========================
# walk-A fwd (g rides x1; f/gv host = the |0> A word) -> closer
# (fv parked signed; A word back |0>) -> plateau row-mul A =
# |fv|*y1*2^{-Tmin} + sign fix (A = y1*x1^{-1}*2^{tstop-Tmin}) ->
# HMR(y1) -> closer_inv (f/gv rehomed to the freed y1 word) ->
# walk-B rev with rider halves (A *= 2^{Tmin-tstop}) -> recompute/
# phase-fix/erase y1 -> swap(y1, A).
# The mul pool and its plan exist only at mul sites: the walk legs
# allocate their own scratch (decode chains, zero-test keepers,
# rider fold anc), so the pool parks at width 0 around them and
# the walk census does not carry idle pool wires.
Walk 1, forward 615,953 Toffolis · peak 935 qubits
with b.phase("s2-walkA"):
mm.off()
inv.start(A, "s2-walkA", "s2-walkB")
Multiply by inverse 272,471 Toffolis · peak 925 qubits
with b.phase("s2-seam"):
mm.on(E.HMR_POOL_PLATEAU, seam=True)
inv.walk.closer()
inv.row_mul(y1, A, ma)
inv.sign_fix(A, ma)
mbits = [b.hmr(q) for q in y1]
inv.row_unpark(y1, ma)
inv.walk.pool.rebind_band("fw", list(y1))
inv.walk.closer_inv()
inv.arm_rider(A, {"rev": "halve"})
mm.off()
Walk 1, reverse 637,032 Toffolis · peak 935 qubits
with b.phase("s2-walkB"):
inv.walk.rider = None
inv.finish()
Phase repair 383,522 Toffolis · peak 935 qubits
with b.phase("s2-recompute"):
mm.on(E._hmr_pool_off())
E.mul_modp(b, x1, A, y1, p, n, ma) # y1 = x1*A = y exact
for q, m in zip(y1, mbits):
b.z(q, cond=m) # Z^b phase fixup
E.mul_modp(b, x1, A, y1, p, n, ma, inv=True)
for yq, aq in zip(y1, A):
b.swap(yq, aq)
# ============ s4: x1 <- x1 + 3*Q.x - lambda^2 = P.x + 2*Q.x - lambda^2
# The fused square subtracts lambda^2 from x1 term by term, so no
# square register survives the phase. The A word is |0> and idle
# here; it is freed so the square's carries can use its width, and
# allocated again afterwards.
x update 64,540 Toffolis · peak 935 qubits
with b.phase("s4-sub-squ"):
mm.pool_width(0)
b.release_recycle("A", A)
b.set_phase("s4-sub-squ")
LC.const_modp(b, x1, S_3x2, singles["flag"], y1, singles["clean3"], p, n)
A[:] = b.qubits(n)
b.claim("A", A)
mm.pool_width(E._hmr_pool_off())
# ================= s6-imul: y1 <- x1 * y1 ==========================
# A = x1*y1 (exact, tape empty) -> HMR(y1) -> walk-C fwd on x1
# (f/gv host = the freed y1 word; rider halves: A *= 2^{Tmin-tstop})
# -> closer -> plateau row-mul y1 = |fv|*A*2^{-Tmin} + sign fix (= y exact)
# -> Z^b -> erase pair -> closer_inv -> walk-D rev with rider
# doubles (A restored to x1*y1) -> swap(y1, A).
Multiply 191,761 Toffolis · peak 935 qubits
with b.phase("s6-mul1"):
E.mul_modp(b, x1, y1, A, p, n, ma)
Walk 2, forward 628,378 Toffolis · peak 935 qubits
with b.phase("s6-walkC"):
mbits2 = [b.hmr(q) for q in y1]
mm.off()
inv.start(y1, "s6-walkC", "s6-walkD")
inv.arm_rider(A, {"fwd": "halve", "rev": "double"})
Recover y 542,580 Toffolis · peak 925 qubits
with b.phase("s6-seam"):
mm.on(E.HMR_POOL_PLATEAU, seam=True)
inv.walk.closer()
inv.row_mul(A, y1, ma) # y1 = |fv|*A*2^-Tmin
inv.sign_fix(y1, ma) # y1 = y exact
for q, m in zip(y1, mbits2):
b.z(q, cond=m)
inv.sign_fix(y1, ma, inv=True)
# erase pair: the facing lifts cancel (the row parks in
# magnitude form through the sign fix + fixup, which never
# touch the row ids; the inv call carries the un-reduce +
# un-lift)
inv.row_mul(A, y1, ma, inv=True)
inv.walk.closer_inv()
mm.off()
Walk 2, reverse 637,032 Toffolis · peak 935 qubits
with b.phase("s6-walkD"):
inv.walk.rider = None
inv.finish()
mm.on(E._hmr_pool_off())
for yq, aq in zip(y1, A):
b.swap(yq, aq)
# ================= s7-final ========================================
Final update 17,084 Toffolis · peak 935 qubits
with b.phase("s7-final"):
LC.neg_modp(b, x1, y1, singles["clean3"], p, n)
LC.const_modp(b, x1, S_x2, singles["flag"], y1, singles["clean3"], p, n)
LC.const_modp(b, y1, S_y2, singles["flag"], x1, singles["clean3"], p, n, inv=True)
# ================= teardown ========================================
Release 0 Toffolis · peak 935 qubits
b.set_phase("close")
b.release("A", A)
mm.off()
for w in reversed(list(A)):
b.recycle(w)
return x1, y1, ox, oy
def emit_fwd(self):
for t in range(self.S):
self.step_fwd(t)
⋯ 438 lines
def step_fwd(self, t):
b = self.b
g = G(b)
with b.phase(self.phase_fwd):
s, bw = self._fwd_add(g, t)
# F5 z += 1 on live lanes (pre-freeze: every lane is live)
early = t < self.Tmin
self._z_step(g, early)
self._fwd_shift_exit(t, early)
# rider stage: armed on every step of the data window
# [Tmin, S). A lane's alive steps are [0, tstop), so
# armed AND alive covers exactly the tstop - Tmin
# data-bearing stages — the walk itself unary-decodes the
# count; a lane still live at the schedule cut fails
# through the cut's own priced channel. Mirror slot:
# between R13 and R14 on the rev leg.
if self.rider is not None and self.rider["legs"].get("fwd") and self.Tmin <= t:
self._rider_stage(self.rider["legs"]["fwd"])
# F7 freeze detect + flips (blocked zero test: the zf
# witness lives through F16, coexisting with the decode
# transients — a plain chain's trail there pushes the
# armed-window census past the walk peak). tst stores the
# freeze index OFFSET t+1-Tmin (values 1..S-Tmin; 0 =
# never fired).
if not early:
# block=16: R13's small-block argument, bounded below
# by the keeper count — unlike R13's witness, zf's
# keeper ladder lives through the decode (F16 releases
# it), so extra keepers land on the decode apex, the
# arena's binding moment. 16 halves the chain apex at
# the widest armed rows (WG <= ~24) without adding a
# keeper; the ztest Toffoli count is block-invariant.
zf, zfk = g.ztest_blocked(self.g.ids + [self.g.sign], block=16)
fire = g.andp(zf, self.frz, pol=(True, False))
b.cx(fire, self.frz)
for i in range(self.Wt):
if ((t + 1 - self.Tmin) >> i) & 1:
b.cx(fire, self.tst[i])
# F8 class flags; frz already flipped, so c3 reads the NEW
# frozen state: a lane that freezes at this step pushes no
# record. Pre-freeze, frz is a constant 0 and drops from the
# chain. Armed steps keeperize the z ztest (one block >=
# Wz): the chain trail would otherwise sit through the
# decode apex — the arena's binding moment — at Wz-1 live
# wires vs the keeper's 1; the F14 recompute is the
# uncompute price (Wz-2 Toffoli per test).
if early:
znz, znzt = g.zero_test(list(self.z))
else:
znz, znzk = g.ztest_blocked(list(self.z), block=self.Wz)
zpos = g.andp(self.z[self.Wz - 1], znz, pol=(False, False))
if early:
c3, c3t = g.and_chain([s, zpos], [False, False])
else:
c3, c3t = g.and_chain([self.frz, s, zpos], [False, False, False])
# F9 j-flags
jf = self._jflags(g, c3)
# (F11 absent: the forward leg never READS the decoded
# bit — bw carries the branch and the record port stores
# it. The j flags remain: F13's store port reads the
# ladder's last rung.)
# F13 R push: brigade rotation up, then port. The span
# clips at t: fill before step t's push is at most the
# number of prior class-3 steps <= t (a theorem).
self._r_fit(t)
sp = min(self.spans[t], t)
for i in range(sp - 1, -1, -1):
b.cswap(jf["j5p"], self.R[i], self.R[i + 1])
b.cswap(jf["j5p"], bw, self.R[0])
# F14 flag/test deaths (no forward decode to kill)
self._jflags_release(g, jf, c3)
g.release_chain(c3t)
g.andm(zpos, self.z[self.Wz - 1], znz, pol=(False, False))
if early:
g.release_chain(znzt)
else:
g.ztest_free(znzk)
# F15 s death; armed steps then run F16
self._s_death(g, s, None if early else fire)
if not early:
# F16 fire death: witness [tst == t+1-Tmin] & (g == 0)
# & frz
eqt, eqtt = g.eq_const(self.tst, t + 1 - self.Tmin)
w1 = g.andp(eqt, zf)
# fire holds w1 & frz — free retirement
g.andm(fire, w1, self.frz)
g.andm(w1, eqt, zf)
g.release_chain(eqtt)
g.ztest_free(zfk)
# F17 b death: HMR; the outcome-conditioned fixup lands on
# the reverse leg's b' at the mirrored step
self.o_fwd[t] = b.hmr(bw)
b.recycle(bw)
⋯
def _fwd_add(self, g, t):
"""Run the forward step entry through F4: arm at t = Tmin, pad
the pairs, mint the sigma root, swap the rows, mint the branch
bit and run the controlled row adds. Returns (s, bw).
"""
b = self.b
sc = self.sched
if t == self.Tmin:
self._arm_alloc()
# width entry: pair pads for the sigma swap
Wm = sc.fg_pair(t)
Vm = sc.v_pair(t)
self.f.grow_to(Wm)
self.g.grow_to(Wm)
self.fv.grow_to(Vm)
self.gv.grow_to(Vm)
# F1 sigma root (frozen lanes have g[0] = 0, so s = 0)
s = g.andp(self.z[self.Wz - 1], self.g.pos(0), pol=(False, True))
# F2 conditional row swap + z negate-conjugation
for fw_, gw_ in zip(self.f.wires(), self.g.wires()):
b.cswap(s, fw_, gw_)
for fw_, gw_ in zip(self.fv.wires(), self.gv.wires()):
b.cswap(s, fw_, gw_)
for i in range(self.Wz):
b.cx(s, self.z[i])
# F3 branch-bit mint
bw = b.qubit()
b.cx(self.g.pos(0), bw)
# F4 branch arithmetic: add headroom, negate-conjugated
# controlled adds ([CDKM] cells, shared gadget)
self.g.grow_to(Wm + 1)
self.f.grow_to(Wm + 1)
self.gv.grow_to(Vm + 1)
self.fv.grow_to(Vm + 1)
u = g.andp(s, bw)
for w_ in self.g.wires():
b.cx(u, w_)
for w_ in self.gv.wires():
b.cx(u, w_)
# the add carry is a step transient (|0> outside the two
# calls), so it is not carried in the arena census
car = b.qubit()
g.ctrl_add_rows(bw, self.f.wires(), self.g.wires(), car, cin=u, qcap=self.qcap)
g.ctrl_add_rows(bw, self.fv.wires(), self.gv.wires(), car, cin=u, qcap=self.qcap)
b.recycle(car)
g.andm(u, s, bw)
return s, bw
⋯ 213 lines
def _jflags(self, g, c3):
"""Compute the j anchor flags. a1 = c3 AND ~fv[2] is the XOR c3 ^ j2
(j2 and a1 partition c3), and a2 = a1 ^ j3 likewise — both
mint by CX pairs (Clifford), not ANDs.
"""
b = self.b
j2 = g.andp(c3, self.fv.pos(2))
a1 = b.qubit()
b.cx(c3, a1)
b.cx(j2, a1)
j3 = g.andp(a1, self.fv.pos(3))
a2 = b.qubit()
b.cx(a1, a2)
b.cx(j3, a2)
j4 = g.andp(a2, self.fv.pos(4))
j5p = b.qubit()
b.cx(a2, j5p)
b.cx(j4, j5p)
return {"j2": j2, "a1": a1, "j3": j3, "a2": a2, "j4": j4, "j5p": j5p}
def emit_rev(self):
self._replay[0] = True
for t in range(self.S - 1, -1, -1):
self.step_rev(t)
⋯ 528 lines
def step_rev(self, t):
b = self.b
g = G(b)
with b.phase(self.phase_rev):
# entry widths = fwd step t's exit windows
self._r_fit(t)
# R1 unfreeze comparator; its AND trail releases BEFORE the
# controlled tst fan changes the trail's literals. Pre-freeze
# steps carry no unfreeze machinery (tst < Tmin+1 is
# unreachable).
early = t < self.Tmin
if not early:
eqt, eqtt = g.eq_const(self.tst, t + 1 - self.Tmin)
fireR = g.andp(eqt, self.frz)
g.release_chain(eqtt)
b.cx(fireR, self.frz)
for i in range(self.Wt):
if ((t + 1 - self.Tmin) >> i) & 1:
b.cx(fireR, self.tst[i])
# R2 sigma witness from the post-state carrier (pre-freeze
# the ~frz literal is constant-1: a plain copy)
if early:
sp_ = b.qubit()
b.cx(self.fv.pos(1), sp_)
else:
sp_ = g.andp(self.frz, self.fv.pos(1), pol=(False, True))
# R3 class flags; the extra ~fireR literal mirrors F8: the
# step at which a lane froze pops no record. Armed steps
# keeperize the z ztest exactly as F8 (the trail would sit
# through the R6 decode apex); R12 recomputes at release.
if early:
znz, znzt = g.zero_test(list(self.z))
else:
znz, znzk = g.ztest_blocked(list(self.z), block=self.Wz)
zpos = g.andp(self.z[self.Wz - 1], znz, pol=(False, False))
if early:
c3, c3t = g.and_chain([sp_, zpos], [False, False])
else:
c3, c3t = g.and_chain([self.frz, sp_, zpos, fireR], [False, False, False, False])
# R4 j-flags
jf = self._jflags(g, c3)
# R5 R pop: port out first, then brigade rotation down
bpw = b.qubit()
b.cswap(jf["j5p"], self.R[0], bpw)
sp = min(self.spans[t], t)
for i in range(sp):
b.cswap(jf["j5p"], self.R[i], self.R[i + 1])
# R6 decode
pred = b.qubit()
anchors, undo = self._decode(g, jf, pred)
# R7 b' assembly: pred ^ s' ^ (fireR & ~s'); the unfreeze
# term is gated by the sigma witness. Pre-freeze there
# is no unfreeze term.
b.cx(pred, bpw)
b.cx(sp_, bpw)
if not early:
fnos = g.andp(fireR, sp_, pol=(True, False))
b.cx(fnos, bpw)
g.andm(fnos, fireR, sp_, pol=(True, False))
# (R8-R10 mismatch-safety machinery absent — see F11.)
# R11 deferred forward-b fixup: demolished value = b' & ~j5p
b.x(jf["j5p"])
b.cz(bpw, jf["j5p"], cond=self.o_fwd[t])
b.x(jf["j5p"])
# R12 decode deaths BEFORE un-run changes the anchors the
# decode read
self._pred_death(pred, anchors)
self._decode_release(g, undo)
self._jflags_release(g, jf, c3)
g.release_chain(c3t)
g.andm(zpos, self.z[self.Wz - 1], znz, pol=(False, False))
if early:
g.release_chain(znzt)
else:
g.ztest_free(znzk)
# R13 fireR death (g still post-state; g == 0 is absorbing,
# so (g == 0) & ~frz witnesses exactly a just-fired lane)
if not early:
# block=8: the armed-window row is narrow (WG <= ~26),
# so small blocks cut the ladder apex (~25 -> ~12 live
# transients) at the arena's binding moment; the ztest
# Toffoli count is block-invariant.
zfr, zfrk = g.ztest_blocked(self.g.ids + [self.g.sign], block=8)
# fireR holds zfr & ~frz — free retirement (before
# ztest_free so zfr is live)
g.andm(fireR, zfr, self.frz, pol=(True, False))
g.ztest_free(zfrk)
# rider stage (mirror slot of the fwd leg's; same
# [Tmin, S) arm; runs before the repack inverse, so it
# sees the same post-repack row state the fwd rider saw)
if self.rider is not None and self.rider["legs"].get("rev") and self.Tmin <= t:
self._rider_stage(self.rider["legs"]["rev"])
self._repack_inv()
# R14 un-run: un-shifts, z decrement, un-adds, at the fwd
# step's grown widths (the mirror law: _rev_unshift)
self._rev_unshift(t, early)
self._z_step(g, early, dec=True)
self._rev_unadd(g, t, sp_, bpw, early)
⋯
def _decode(self, g, jf, pred):
"""Decode payload. Identical cubes across strata mint their
AND chain ONCE (keyed by base wire + cube); every payload
hangs off the shared leaf. Anchors stay per-payload.
"""
b = self.b
anchors = []
undo = []
cache = {}
b.ccx(jf["j2"], self.gv.pos(1), pred)
b.ccx(jf["j2"], self.fv.pos(3), pred)
anchors += [(jf["j2"], self.gv.pos(1)), (jf["j2"], self.fv.pos(3))]
for j in (3, 4):
base = jf["j3"] if j == 3 else jf["j4"]
for cubes, term in self.grouped[j]:
for cube in cubes:
key = (base, tuple(sorted(cube.items())))
if key in cache:
leaf = cache[key]
else:
ks = sorted(cube)
wires = [base] + [self.f.pos(1 + v) for v in ks]
pols = [True] + [bool(cube[v]) for v in ks]
if len(wires) == 1:
leaf, trail = base, None
else:
leaf, trail = g.and_chain(wires, pols)
cache[key] = leaf
undo.append(trail)
if term[0] == "const":
b.cx(leaf, pred)
anchors.append((leaf,))
else:
kind, i = term
w = self.gv.pos(j - 1 + i) if kind == "ag" else self.fv.pos(j + i)
b.ccx(leaf, w, pred)
anchors.append((leaf, w))
return anchors, undo
⋯ 6 lines
def _pred_death(self, pred, anchors):
b = self.b
m = b.hmr(pred)
for a in anchors:
if len(a) == 1:
b.z(a[0], cond=m)
else:
b.cz(a[0], a[1], cond=m)
b.recycle(pred)
def _subtract_square(b, lam, out):
"""out -= lam^2 mod p with the engine's FusedSquare: the low, high and
cross squares each fold into out and clear before the next starts.
"""
sq = E.FusedSquare(b, lam, out, QCAP)
b.set_phase("squ-a-lo")
sq.low_term()
b.set_phase("squ-b-hi")
sq.high_term()
b.set_phase("squ-c-sum")
sq.cross_term()
sq.close()
⋯
class FusedSquare:
"""out -= lam^2 mod p in three square terms (low, high, cross).
Call low_term(), high_term() and cross_term() in any order, then
close(). `cap` is the arena ceiling the component must not exceed.
"""
def __init__(self, b, lam, out, cap):
assert len(lam) == N and len(out) == N
self.b, self.out, self.cap = b, list(out), int(cap)
self.l0, self.l1 = list(lam[:SQ_HALF]), list(lam[SQ_HALF:])
self.margin = _square_margin()
self.one = b.qubit()
b.x(self.one)
def room(self):
return self.cap - self.b.active_qubits
# -- square of v into a fresh 2n-wire register ---------------------------
def _rows(self, v, prod, clear):
"""Build prod = v^2 - 1 from |0> (clear=False), or return it to |0>
(clear=True). Row i adds 2^m + (2 v_i - 1) R + v_i at offset 2i+1,
with R = v >> (i+1) and m = len(R): the R wires are complemented
when v_i = 0, the row's addend ends with v_i itself, and the carry-in
is 1. Row i's carries borrow the zero wires above its window.
"""
b, n = self.b, len(v)
top = prod[2 * n - 1]
order = range(n - 1)
if clear:
b.cx(v[n - 1], top)
b.x(top)
order = reversed(order)
else:
for j in range(n):
b.cx(v[j], prod[j])
b.x(prod[j])
for i in order:
ctl, rest = v[i], v[i + 1 :]
window = prod[2 * i + 1 : n + i + 2]
b.x(ctl)
for q in rest:
b.cx(ctl, q)
b.x(ctl)
if clear:
for q in window:
b.x(q)
_ripple_add(
b, window, rest + [ctl], cin=self.one, room=self.room(), spares=prod[n + i + 2 :]
)
if clear:
for q in window:
b.x(q)
b.x(ctl)
for q in rest:
b.cx(ctl, q)
b.x(ctl)
if clear:
for j in range(n):
b.x(prod[j])
b.cx(v[j], prod[j])
else:
b.cx(v[n - 1], top)
b.x(top)
# -- folding a shifted slice into out ------------------------------------
def _short(self, val, shift, sign, cin):
"""out += sign * 2^shift * (val + cin), no reduction: the slice ends
`margin` bits below bit 256 and the carry stops there.
"""
b = self.b
window = self.out[shift : shift + len(val) + self.margin]
if sign < 0:
for q in window:
b.x(q)
_ripple_add(b, window, val, cin=cin, room=self.room())
if sign < 0:
for q in window:
b.x(q)
def _modular(self, val, shift, sign, cin):
"""out += sign * 2^shift * (val + cin) mod p for a slice that ends at
bit 255: add with carry-out c, add F when c is set, then erase c
by measurement and recompute it from the top `margin` bits as a
phase ([out < slice] exactly when the add wrapped).
"""
b, out, t = self.b, self.out, self.margin
assert shift + len(val) == N
if sign < 0:
for q in out:
b.x(q)
carry = b.qubit()
_ripple_add(b, out[shift:], val, cin=cin, cout=carry, room=self.room())
fold_window = out[: SQ_FOLD.bit_length() + t]
assert self.room() >= len(fold_window) - 1, "fused square: ceiling too low"
_add_scaled_bit(b, fold_window, carry, SQ_FOLD)
m = b.hmr(carry)
b.recycle(carry)
assert self.room() >= t, "fused square: ceiling too low"
_phase_if_below(b, out[N - t :], val[len(val) - t :], m)
if sign < 0:
for q in out:
b.x(q)
def _fold(self, val, shift, sign, cin):
"""out += sign * 2^shift * (val + cin) mod p for any slice: the part
at or above bit 256 re-enters at the signed digits of F.
"""
low, high = val[: N - shift], val[N - shift :]
if _is_short(shift, len(low), self.margin):
self._short(low, shift, sign, cin)
else:
self._modular(low, shift, sign, cin)
for j, d in SQ_FOLD_DIGITS if high else ():
self._fold(high, j, sign * d, None)
# -- the three terms ------------------------------------------------------
def _term(self, v, folds):
b = self.b
prod = b.qubits(2 * len(v))
b.claim("square/product", prod)
self._rows(v, prod, clear=False)
for shift, sign in folds:
self._fold(prod, shift, sign, self.one)
self._rows(v, prod, clear=True)
b.release("square/product", prod)
for q in reversed(prod):
b.recycle(q)
def low_term(self):
"""out -= (1 - B) * l0^2."""
self._term(self.l0, _folds_low())
def high_term(self):
"""out -= (F - B) * l1^2."""
self._term(self.l1, _folds_high())
def cross_term(self):
"""out -= B * (l0 + l1)^2; the sum forms in place on l1 plus one
fresh top wire and is undone afterwards.
"""
b = self.b
spill = b.qubit()
s = self.l1 + [spill]
_ripple_add(b, s, self.l0, room=self.room())
self._term(s, _folds_cross())
for q in s:
b.x(q)
_ripple_add(b, s, self.l0, room=self.room())
for q in s:
b.x(q)
b.recycle(spill)
def close(self):
self.b.x(self.one)
self.b.recycle(self.one)
⋯ 28 lines
def _square_margin():
"""Return the truncation margin t in bits: each accumulation fails with
probability at most 2 * 2^-t (short: carry escape, wrap past p;
modular: F-window escape, top-bit tie), and the sum over all
accumulations of the three terms stays within SQ_FAIL_SHARE.
"""
t = 1
while True:
ops = 0
for width, folds in (
(2 * SQ_HALF, _folds_low()),
(2 * SQ_HALF, _folds_high()),
(2 * SQ_HALF + 2, _folds_cross()),
):
ops += sum(_fold_events(width, shift) for shift, _ in folds)
if 2 * ops * 2.0**-t <= SQ_FAIL_SHARE:
return t
t += 1