Logical gadgets › the design

Logical QCCD gadgets — plan and beta#

Goal. Compile a LogicQ logical program over QLDPC codes onto a QCCD architecture at the scale of 10⁴ ions, and let a person read the result at every level: the logical instructions; the gadgets that realise them, drawn as black boxes with ions streaming in and out; and, inside any one box, the ion-by-ion hardware program, step by step.

The one idea: never flatten. Each logical cycle, logical operation and magic-state factory is a gadget, a black box with ion ports. A gadget is compiled and verified once per master, then characterized into an abstract: duration, port transfer windows, heating. A machine instantiates masters many times. The whole-machine schedule is computed over the abstracts alone. Ion-level programs exist once per master, and the tool replays one on demand for whichever instance the user opens. This is how integrated circuits scale past 10⁹ transistors, and the correspondence is close enough to borrow the vocabulary (§1).

Written 2026-09-16 on the compiler branch. Status: the beta (§5–§8, BB codes at 9,720 ions) is verified as hardware only; the verified place library (§9: surface code, d = 3) is verified as hardware and as logic, and composes into logical algorithms that are signed off end to end. Every number below is measured.

python -m qccd.gadget places                         # the verified places, and what was checked
python -m qccd.gadget algorithm PROGRAM.alg                 # schedule + sign-off
python -m qccd.gadget showcase                       # beta: 96 BB[[72,12,6]] blocks, 9,720 ions
python -m qccd.gadget build PROGRAM.lq [--studio]    # beta: a LogicQ program in the accepted subset
python -m qccd.gadget build PROGRAM.lq --design edited.gadget.json   # a design from the editor
# open out/gadgets/<name>/index.html

1 · What IC design already solved#

IC designHereWhat carries over
standard cell, hard macroleaf gadget: a device fragment plus one TSIR per operationthe layout exists once per master; every instance shares it
cell abstract (LEF: size, pins)footprint, ports (boundary node, side, direction, role, width)a parent sees only the abstract
timing library (Liberty arcs)op characterization: duration, per-ion port crossing times, n̄ at the port, gate counts, rule reporta parent schedules against it
characterization, SPICE → .libreplay the op's TSIR under the cost model and run all 28 rulesnumbers come from the verifier, never from a formula written beside it
testbench with ideal sources and loadsport stubs: ideal ion source/sink sites just outside the boundarya leaf is verified in isolation
structural module, netlistcomposite gadget: instances, channels, exported portsrecursion to any depth
bus, bit orderion bundle crossing one port in a declared orderpairing two bundles is a bit-order check (G1)
wire-load model, RC extractionchannel priced by replaying a conveyor of its lengtha channel is not a cell
static timing analysisarrival vs. intake at every port; n̄ along every pathG-rules (§8)
elastic / latency-insensitive pipelinespaced handshakes: a source may stall, a channel holds at most its length§7
mixed-level simulationgadget-level execution plus ion-level replay of one instancedrill-down
descend / returndouble-click an instance / Escthe navigation model
HLS scheduling and bindinglogical program → gadget schedule: bind, schedule, routesynth (§7)
DRAM refreshsyndrome extraction: a block that stops cycling loses its contentsthe refresh constraint (G8)
hierarchical DRC/LVS, flat sign-offG-rules on the composition; flatten + 28 rules on small designsthe abstraction is checked, not trusted (§9, planned)

2 · Concepts#

Master. A named gadget definition: kind is leaf or composite. It has ports, a resting inventory of ions by role, a capacity, a footprint and ops.

Port. Where ions cross the boundary. dir in / out / inout; role (data, messenger, magic, any); width, the ions one transfer carries; order, the order a bundle crosses in; and on a leaf the device node it sits on and the testbench stub beyond it. A composite's port binds to one child port.

Op. Something a master does. On a leaf, an op is one TSIR on the master's testbench device. Every op carries a characterization and a status, under the repository's reporting contract (passed only if the check ran):

statusmeaningdrill-down
verifiedthe TSIR replays, no rule fails, and it ends in the placement it promised; R10 reported skipped with the verifier's reasonion by ion
certifiedas verified, plus R10 passed: the proved Lean checker accepts the certificate and the emitted pulses compose to the circuit (none yet)ion by ion
modeledno TSIR; an analytic abstract with its source named (none used)none, and the tool says why
faileda TSIR that breaks a rule or its placement contract; G9 fails any schedule that uses ition by ion, violations listed

Instance. A master placed in a composite. Instances share their master's programs; an instance adds only a position and the global ids of its ions.

Channel. A transport link between two ports: a Manhattan route, and a length in sites (route length − 1). Every carry is priced by replaying chan<L>, a conveyor of that length, memoized per length.

Control domain. Every leaf instance and every channel has its own control electronics, so R4 and R22 (one class, one waveform per cycle) hold within a domain, and concurrency across domains is the scheduler's business. This is a modelling assumption, stated rather than hidden: a modular machine with per-module DAC groups (§14).

Placement contract. A leaf op starts from the master's home placement and ends in the placement it declares, checked by characterization. Compiled syndrome rounds do not do this on their own: the BB [[72,12,6]] round from the compiler ends with the whole ring rotated 45 of 72 slots, warm. So cycle appends the restoring rotation and a cool, and its characterization pays for both. That is what makes cycle × N a legal thing to write.


3 · The interface contract#

A transfer is one ion bundle crossing one port during one op, measured from the replay: port, dir, count, the local ion names in crossing order, each ion's crossing time from op start, and the largest n̄ among them (read with the replay's probe).


4 · Execution: events, carries, ions#

The compiled machine program is a GIR (gir.json, schema qccd.gir/0.1):

Viewing a composite shows its children's events and the carries on its channels; viewing a leaf replays the master's TSIR for the op running at the global time, at local time t − t0 (modulo the op inside cycle × N, and through the warp). Every level reads the same event list.

4.1 Roads are devices; heat crosses the town#

A road is modeled the way a shuttle inside a place is. It is the conveyor chan<L>: L sites one lattice unit apart along the road's path, a stub at each end on the doors it joins. Every carry on the road runs that device's one-ion carry program. Its timing is that program's replay (LeafLibrary.channel_timing), and so is its heat. The page draws the road as its sites. An ion on the road is placed where that program has it at the ion's own local time (LeafSim, the replay that places ions inside a place), and it queues on the last sites until it is taken in. A junction is the device junc9: a centre, four arm traps and four stubs. Up close it is drawn as that device, with its ions inside while a pass runs.

A bundle crosses a junction one ion at a time (user's decision, 2026-10-26). The rest of the bundle waits on the road's sites, and each ion makes the junction's lone-ion pass (pass.A.B.1, its own TSIR) as the road delivers it. The bundle is never stacked as one chain on the arm's stub. The stacked bundle pass (pass.A.B) is still in the library, but splitting that chain heats every waiting ion: 9.5 to 15.5 quanta each, against 3.4 for an ion crossing alone. Following heat across the town showed that stacking made trips between streets infeasible.

A carry is therefore taken in ion by ion. Place check G1 and machine rules G1 and G12 read it that way:

For a place taking a whole bundle, this is the same check as before.

Heat is followed across the town. An op is characterized from cold. Its replay adds what every move costs, and a cool sets the ions it cools to 0. One cold replay therefore says what any arrival does:

A road adds its conveyor's heat, and a junction pass adds its own (it cools no one): 3.4 quanta per ion, each crossing alone. follow(gir) walks every ion, residents included, through its rows in the order it meets them.

Place check G6 and machine rule G6 both read the followed heat: no ion may pass the ion-loss limit (510 quanta) anywhere on its way. A two-qubit gate that meets an ion still carrying its trip would fail G9; none does, because every op cools before its gates.

Before the heat was followed, a used bundle bound for a far depot reached n̄ ≈ 630 on the Shor floorplan, after 27 stacked junction passes. Every op had seen it as cold. Four changes keep every run under the limit:

4.2 The basic rules between gadgets (H1)#

A gadget looks like a higher level, but the chip is one. What carries ions between gadgets is driven by DACs and electrodes like what moves them inside one, and it obeys the same basic rules. That is what the user asked for on 2026-10-26: the rules apply to the hardware program when there are gadgets.

Each place's op is verified on its own device against R1-R23: a cycle is one waveform of its plane (R4, R22), one class a cycle, holds by the per-site switches (R4d), and so on. Every leaf declares a broadcast plane: 32 shared channels (6 + 6 + 4 electrode roles, each with a motion bank and a hold bank), compensation channels, and a switch per site. Between the places, the schedule used to price a road by its conveyor's one-ion program, run once per ion. That is not a program a road can play:

Every carry is one program on its road's own device:

A door's stub is driven by one side at a time. The road never loads while the op on the near side puts an ion on the stub. It never delivers while the op on the far side takes one off.

The schedule is timed by these programs. City._road gives each ion's arrival from the road's compiled program, and City._door_start moves the receiving op until every take follows its delivery without overlapping another.

Rule H1 (place check and machine rule road_ok(c)) compiles every carry, memoized by shape, and asks three things:

Every shipped schedule passes. A schedule that takes an ion off a long road before the road can bring it fails, and so would the old per-ion pretence, under R22.

A road is its real geometry. A road on a floor is a polyline. Its legs are its straight runs, each a whole number of sites, with a site at every corner. A straight road of L sites is chan<L>, as before. A bent one is its own conveyor, named by its legs: chan3R10 is three steps, a right turn, then ten. The schedule, heat, H1 and the modules all read the shape (Net.shape; a bent road's GIR row carries it as a sixth field).

The rules then say what a corner costs. A hop along x and a hop along y are different electrode roles, hence different waveforms (R22). A rigid shift with ions on both sides of a corner is two cycles, the leg nearer the far end first (split_shift), and the road's program spends them. The cost model charges a corner hop like a straight one, so a single ion crossing a bent road takes as long as on a straight road of the same sites.

The page plays each carry's compiled program. roads.attach_runs gives every body its carries' programs (road_runs, indices into the root's road_programs): each cycle's class, and which ion moves from which node to which. The page draws an ion where that program has it, from when the near op puts it on the stub until the far op takes it off, and draws a road's sites at its real nodes, corners included. It is the very program H1 judged, not the one-ion pretence.

The machine's wiring is counted (machine_census): electrodes, switches and DACs for every device the floor holds, each driving its own plane as its master declares. On the town, the 60 roads and 32 junctions take 3,054 of the machine's 3,438 DACs. That is the price of every road having its own plane. §4.3 lets a design share planes instead.

4.3 Shared planes (H2)#

A floorplan may say that devices share one plane of DACs, or one laser:

plane bus  dacs   roads                  # every road on one set of channels
plane mems dacs   memory                 # every memory (a family word)
plane beam lasers post bridge clinic     # three places under one beam

Members are places, junctions and roads by name, roads, junctions, or a family word. A shared plane is one set of channels, so its rule is a single device's rule: one waveform at a time. Two members may play a cycle at once only if it is the very same cycle: the same class (or gate), the same lab directions, the same start and end.

The scheduler makes members take turns. Each group of planes (planes that share a device form one group) has an activity calendar beside the devices' own. Those still hold occupancy, such as a block resting in a zone, which plays no waveform.

Rule H2 (the place check, and the machine rule plane_conflicts() = 0) reads the composed program: every op's TSIR cycles at their real times, and every road's compiled program. It reports any instant at which two members play different cycles. A schedule made without the plane fails it.

G4 and crowd now count the ions on a road's sites from its compiled program. An ion waiting on a door's stub, for the road or for the far side, is on the stub, whose room is the bundle's (R1 in the road's program). The machine model follows an ion through the near stub, the road and the far stub, by the same program.

What it buys (machine_census counts a shared plane's broadcast channels once):

floor, programplanesDACsmakespan
town, bell_cnotnone3,438163.2 ms
town, bell_cnotbus dacs roads1,550196.1 ms
town, teleportnone3,438536.3 ms
town, teleportbus dacs roads1,550571.3 ms
town, teleportmems dacs zone1 zone2 zone33,374536.3 ms
town, teleportbeam lasers post bridge clinic3,438 (101 beams, not 103)572.9 ms
full stack, Shor-15 (folded)none6,8269.28 s
full stack, Shor-15 (folded)bus dacs roads2,98611.00 s
full stack, Shor-15 (folded)mems dacs memory6,60210.08 s

Every row holds every check. On the town, a bus of every road saves 55% of the DACs at 7-20% more time. That is the co-design trade-off in numbers.


5 · The beta library#

5.1 Leaves (all ops verified)#

masterdeviceops (duration)
mem_bb72ring(36, 2, 24, dock_offset=1): 72 data ions, 24 ancillas in dock traps; corner port traps P_bus (data bundle) and P_net (messenger)cycle 53.9 ms (432 CX contacts, then restore + cool) · layer1q 2.0 ms · emit 19.3 ms · absorb 26.5 ms · visit.<B><i> per logical operator, compiled on demand (12–29 ms for supports of 6–28 ions)
tcx72a comb: two 72-site conveyor rails over 72 gate trapscx / xc 37.0 ms (72 CX in one batch; bundles in at n̄ ≈ 1, out at n̄ ≈ 221)
res4four traps in a rowrelease.Z/X 0.36 ms (reset, H for X, cool, out) · accept.Z/X 0.43 ms (in, cool, H for X, measure)
xjuncone degree-4 junction, four armspass 0.14 ms
fac_t15ring(10, 2, 1, dock_offset=1): 15 ions, one gate dock, a corner port trapproduce 178.5 ms (the [[15,1,3]] encoder, transversal T, decoder, fold, 14 syndrome measurements: 66 CX) · consume 1.4 ms
chan<L>L conveyor sitescarry: one ion takes (L+1) × 5 µs

The memory's cycle is qccd.compile.compile_code (pure Python, ~2–4 s) on the checks LogicQ's matrices define, so any bivariate-bicycle polynomial LogicQ admits compiles. It is a contact schedule: every contact is a CX between the data ion and the check's ancilla, without the extraction circuit's H layers and CX orientation, so R10 cannot be decided on it (noted on the op). The factory's circuit sequencing is a small ring circuit compiler in which every gate leaves the ring a rigid rotation of what it was.

5.2 Composites#

masterchildrenports
tile_bb72mem, res (res4), jx (xjunc)bus (data), west, east (messenger bus)
pair_bb72two tiles, st (tcx72) between their data portswest, east
row_bb72_<P>P pairs, buses joined end to endwest, east
proc_bb72_<R>x<P>R rows; a spine of xjunc on the west; a fac_t15 on every row's east end—

6 · From LogicQ to gadgets#

6.1 The text accepted#

A program file (.lq) is a sequence of statements, each in LogicQ's own surface syntax as LogicQ's parsers read it: // comments; statements split on newlines and ; (a code … { … } declaration runs to its brace); q[i] is logical qubit i of block q. A statement LogicQ would refuse is refused here too, with its line and the reason.

decl    = "code", ident, "as", "BivariateBicycle", "{", "l","=",nat,";", "m","=",nat,";",
          "A","=",poly,";", "B","=",poly,";", ["params","=","(",nat,",",nat,",",nat,")",";"], "}"
        | "code", ident, "as", "Bare" ;
logical = "Logical", ( ("H"|"S"|"T"|"X"|"Z"), qubit | ("CNOT"|"CZ"), qubit, qubit
                     | "measure", mtarget, "->", cvar
                     | "blockTransversal", block, ("H"|"S")          (* the Tutorial's forms *)
                     | "transversalBatch", block, "->", block ) ;
mixed   = "transversal", nat, ("H"|"S") | "transversalCNOT", qubit, qubit, boolmat
        | "pauli", ("X"|"Y"|"Z"), qubit | "magic", "T", qubit
        | "ppm", ( cvar, ":=", "M", mtarget | "frame", P, "(", qubit, ")" | "discard", qubit | "skip" )
        | "blockTransversal", block, ("H"|"S") | "transversalCNOTBatch", block, block ;   (* the paper's *)
mtarget = qubit, "↦", ("X"|"Y"|"Z"), { ",", qubit, "↦", ("X"|"Y"|"Z") } ;   (* "|->" also read *)

What the hardware needs is re-derived the way LogicQ derives it, so the two tools cannot disagree about what a program means (tested against the Tutorial's table):

Blocks with equal matrices share one code identity (the Tutorial's name, e.g. bb72, or bb<n>_<digest>), so they share one memory master.

6.2 Realisation#

instructiongadget opswhat is claimed
code q as …bind block q to the next memory of that code, row-major—
pauli P q[i], Logical X/Z q[i], ppm framea Pauli-frame update: zero timeexact
ppm c := M …, Logical measure (one or more blocks, one logical per block, all X or all Z)res.release.B → bus → mem.visit.Bi (→ bus → next block's visit) → bus → res.accept.B, --ppm-rounds timeshardware verified; one bare ancilla is not fault-tolerant against hook errors (an obligation)
transversalCNOTBatch, Logical transversalBatchemit both blocks → channels → tcx.cx (or xc) → channels → absorb bothhardware verified; that it is CNOT on all k pairs is a CSS fact LogicQ checks
transversal b H/S, blockTransversal, Logical H/S on k = 1mem.layer1qrefused when checkTransversal refuses it (e.g. H on bb72)
magic T q[i], Logical T q[i]fac.produce (scheduled so the state is ready when needed) and one messenger: res.release.Z → mem.visit.Zi → fac.consume (CX from the T ion, then the T ion measured in X) → res.accept.Zhardware verified; distillation statistics (35p³) are the protocol's claim; the S̄ correction is classical (drawn as a frame update)
Logical H/S/CNOT/CZ on single logicals of k > 1 blocks, single-incidence transversalCNOTrefused with the reason: these are LogicQ's PPM gadgets with an ancilla logical (planned)—
mixed-basis or Y measurementsrefused: the messenger would need a basis change—
automorphism, switch, parallelPPMrefused: no text form in LogicQ—

LogicQ lowers magic, switch, automorphism and multi-round surgery to nothing physical today; they are typed obligations. The gadget layer is where magic first becomes ion movements, so every row above names what is verified and what is assumed.


7 · Synthesis#

synthesize(program, library, leaves) → GIR, deterministic:

  1. Flatten the hierarchy into leaf instances and nets (both ends of every channel resolved through composite port bindings); give every resting ion a global id.
  2. Bind blocks to memories of the same code, row-major.
  3. Schedule instructions in program order. A memory's ops start only at its own cycle boundaries, never before its previous op ends, and are followed by cycles_between extraction cycles (default 1) before the next. Shared resources (stations, reservoirs, junctions, channels, factories) keep interval calendars, so instructions on disjoint blocks run concurrently.
  4. Paced handshakes. A bundle handed from op U through a channel of L sites to op D: each ion enters the channel no earlier than U's program sends it (U may stall), no earlier than the ion L places ahead has been taken by D (so the channel never holds more than L), and arrives before D takes it in (else D starts later). Stalls become the event's warp. The station's exit stalls once for both bundles, since both leave in the same cycles.
  5. Routes are shortest paths over nets and junctions, reserved hop by hop and shifted later as a whole when any hop is busy (circuit switching: no deadlock). A messenger that must wait for a memory's cycle boundary waits in the last channel, which stays reserved.
  6. Factories produce as early as their calendar allows; a T request takes the factory whose state is ready soonest counting the trip, and the messenger is released to arrive about then.
  7. Refresh: every gap in every block's timeline is filled with cycle × N.

8 · Hierarchy checks#

the schedule checks, reported as the rule report is: passed only if the check ran. Each rule is also broken on purpose by a test that must fail.

check
G1every channel joins compatible ports (role, width, code, direction); every bundle reaches a station in the order it pairs and comes home in the order the ring expects
G2ion by ion, in time: every ion leaves an instance only after it has arrived there, and ends where it started
G3no instance ever holds more ions than its capacity, or gives away more than it holds
G4a channel never holds more ions than it has sites, nor carries ions both ways at once
G5an instance runs one op at a time
G6no ion crosses a port hotter than the ion-loss limit (~85 uncooled junction round trips, arXiv:1210.3655, at 3 quanta a transit)
G7every logical instruction is discharged, in program order on each block
G8no block goes longer than one cycle without extraction outside a logical op
G9every leaf op the schedule uses is verified (or certified)

9 · Verified places and logical algorithms#

The beta proves its gadgets are runnable; it does not prove they compute anything: its syndrome round is a contact schedule with no H layers or CNOT orientation, and its parities are single-shot bare ancillas. This section is the rebuild around correctness. It fixes one code, the rotated surface code at d = 3, the textbook home of lattice surgery, and makes every gadget a place whose program is checked twice: as hardware (the 28 rules, §5) and as logic (below).

9.1 Places, by category#

A gadget is a place many ions go to for one job; blocks travel between places as people travel between a school, a hotel and a hospital. Every category has its own colour and its own silhouette, defined once and drawn identically by the tool, the landing page and the SVG renderer.

categoryplacesilhouettemasterops
Logical Preparationschoolhouseprep_d3prep.Z, prep.X
Syndrome Extractionclinicoctagonse_d3se.1, se.3
Logical Zonehotelvaultzone_d3check_in, check_out
Logical Memorywardvaultmem_d3store, refresh, release: a block corrected while it rests (ARCHITECTURE.md §7)
Logical Operationworkshophexagontcx_d3cx
Lattice Surgerybridgearched bridgels_d3zz, xx
Logical Readoutpost officestadiumread_d3read.Z, read.X
Magic State Factoryfactorysawtooth roofmsf15produce
Magic State Injectionpharmacychevroninj_d3inject, inject.Y
Ion Depotstationgaragedepot9load.1/8/9, dump.9
Roads and Junctionsroaddotjunc9pass.<a>.<b>[.<k>]
Decodercontrol roomchip with pinsdec_d3decode (§10)
Classical Memoryarchivedrum of cellsarch1record, update, resolve (§10)

The bay. Every ring place is one device family: a racetrack of 2W storage slots, dock traps on outward spurs (so the two rows never share a trap position), and doors on the corner slots, each a spur to a port trap and a stub. Blocks enter at the left doors and leave at the right ones, so with the ring turning clockwise a place is FIFO. Residents (ancillas, a surgery seam, factory staff) live in the docks or on the ring and come home at the end of every op. Ions inside are cooled by name every few arrivals and departures, since a broadcast cool is illegal while an ion waits on a stub (R6): the hottest ion in any place op is n̄ ≈ 131 (lattice surgery peaked at 546 before).

The conveyor round. Ancillas stay in their docks; data ions ride the ring. At every step each data ion standing beside the dock of the check it is due at next — and whose earlier contacts are done — docks, all docked pairs take one batched CX, and the ring turns one slot. The schedule it realises is the Tomita-Svore four-layer order (X checks NW-NE-SW-SE, Z checks NW-SW-NE-SE) recorded as a partial order (each ancilla's contacts in sequence; on each data ion, contacts with checks of different type in layer order); any execution respecting it is the layered circuit up to commuting gates, so the machine never has to stand in layers. place() searches data slots and dock assignments for the shortest round (seeded).

Lattice surgery merges two 3×3 patches through a resident seam into a 7×3 (Z̄Z̄) or 3×7 (X̄X̄) patch of the same rules (surface.merged_patch): seam in |+⟩ (|0⟩), three merged rounds by the same conveyor compiler, seam measured in X (Z). The outcome is the product of the four seam checks of the measured type.

9.2 The logic checks#

The logical half is pure Python, like the rest of the site build.

place opflowsfault distancetime
prep_d3.prep.Z/X9, rank 9/9≥ 327.9 ms
se_d3.se.1 / se.318, rank 18/18≥ 3 (400 / 1,200 faults)31.0 / 56.9 ms
zone_d3.check_in/out18 identity—3.0 / 2.8 ms
tcx_d3.cx40, rank 36/36≥ 35.2 ms
ls_d3.zz / xx36, rank 36/36≥ 3 (3,168 / 3,222 faults)203.5 / 197.4 ms
read_d3.read.Z/X10, rank 9/9≥ 38.4 ms
inj_d3.inject / inject.Y10, rank 10/10 · 9, rank 9/91, by design (injection is not FT)28.6 / 28.7 ms
msf15.producestate vector: T†\+⟩ exact1–2 T errors caught; 35p³
dec_d3.decodelookup table: 136 entries, 16 syndrome bitscorrects all 400 single faults of se.11 µs (modeled)
arch1.record/update/resolvechecked where used: the sign-off reads its frames—0.1 / 0.1 / 0.2 µs (modeled)

Every one of these is also verified as hardware. Cross-checks: the tableau agrees with stim's has_flow on random circuits; every place's fault distance equals stim's search_for_undetectable_logical_errors; a CNOT turned around, a dropped Hadamard and a missing reset fail the flows; a hook-unsafe order passes the flows and fails the distance with a two-fault witness.

9.3 Logical algorithms#

A program over blocks, one instruction per line: prep q Z|X, se q 1|3, cx c t, zz a b -> m, xx a b -> m, read q Z|X -> m, inject t, store q, s q (the S̄ gadget, §10), x|y|z q (a Pauli kept in software, §10) and decode [q …] (call the decoder, §10), each optionally @ instance and each but decode optionally guarded by if <condition>: (§10). Its ideal circuit is the obvious one on one qubit per block, with inject as a free input: the factory is verified by state vector, everything after it is Clifford.

The town is the design they run on: every place in two rows between three streets; top doors take a vertical channel (≥ 9 sites, room to queue a bundle) to a junction on the street above, bottom doors one below; end junctions join the streets. The scheduler runs instructions one after another: shortest routes over channels and junctions, junctions passing a bundle when free, handshakes at every door from the ops' measured per-ion crossing times, depots as sources and sinks, and any block the next instruction does not use parked in a free zone. The GIR is the beta's format plus "model": "places", outcomes and stock.

The checks. The place checks re-derive G1–G9 for the place model, and adds G10 and G11 for the classical half (§10) (§8, with loaded and dumped ions in G2, the op's peak n̄ in G6, and a coherence budget between syndrome-extracting ops in G8). The flattening is the LVS that §9 of the plan promised: every event's circuit is renamed onto global ions and every gate, measurement and reset placed at its absolute time (exact: instructions on one ion never overlap, others commute); the flat circuit is simulated once; outcomes and frames are decoded from the gadgets' own flow reports; and the result must (1) satisfy every relation of the ideal circuit up to declared frames, (2) carry the same number of random bits, (3) carry every ideal flow from inputs to surviving blocks, and (4) for Clifford algorithms, keep distance 3 as a whole schedule (detectors: deterministic parities no logical Pauli inserted between gadgets can flip; surviving blocks read by ideal decoders).

algorithmblocksionsmachine timeflat circuitrelation / flowschedule distance
bell_cnot279194.4 ms34 ions, 50 recordsZ̄₀ ⊕ Z̄₁ = 0≥ 3 (1,798 faults)
bell_surgery279309.3 ms49 ions, 97 recordsm ⊕ Z̄₀ ⊕ Z̄₁ = 0≥ 3 (4,058)
ghz3388215.3 ms43 ions, 59 recordsZ̄₀ ⊕ Z̄₁ = 0, Z̄₀ ⊕ Z̄₂ = 0≥ 3 (1,951)
teleport388592.6 ms66 ions, 176 records1 → X̄_b ⊕ m₂≥ 3 (8,116)
magic_state170295.1 ms25 ions, 32 recordsX̄_t → X̄_t, Z̄_t → Z̄_t (from the magic ion)injection: 1 by design
magic_teleport388816.8 ms74 ions, 192 recordsX̄_t → X̄_b ⊕ m₂; Z̄_t → Z̄_b ⊕ m₁ ⊕ m₃ ⊕ m₄injection: 1 by design
frames279121.8 ms34 ions, 42 recordsm = 1, n = 1 (both flipped by a frame the archive holds)≥ 3 (1,290)
s_gate188545.1 ms74 ions, 185 recordsm = 1 (two S̄ gadgets make a Z̄)injection: 1 by design
cond_cx388159.4 ms2 branchesm=0: u = 0 · m=1: u ⊕ v = 0≥ 3 per branch
teleport_fix388568.2 ms2 branchesr = 0 in both branches≥ 3 per branch
t_gate288725.5 ms4 branchesX̄[t] → X̄[q0] / Ȳ[q0]; Z̄[t] → Z̄[q0], exactlyinjection: 1 by design

(Makespans are the sequential policy's, re-measured 2026-10-20 when split and merge became charged for what a move does rather than what its class is called, and 2026-10-26 when heat was followed across the town (§4.1: junctions pass one ion at a time, places with lasers cool what comes in, readouts recool); ARCHITECTURE.md §7 has the parallel one. t_gate now applies X̄ to the magic block first: the factory makes T†|+⟩, and without it the program was never T -- the contract checker found it, ARCHITECTURE.md §6.)

All pass G1–G11; scheduling, checks and sign-off take under 0.3 s each warm (a guarded program takes one sign-off pass per branch). The checks fail where they should: a Bell schedule against a |0̄0̄⟩ ideal, a GHZ schedule against cx q2 q0, swapped ions in a carry (G1), a dropped event (G7), overlapping ops (G5).

Not established: logical error rates (distance is counted, no noise is sampled), Clifford errors in the factory, measurement crosstalk, and throughput — the town serialises instructions, and since the classical layer landed it serialises road trips too, so no bundle ever queues in a channel shorter than itself (G4).


10 · The classical half: decoder, memory, wires, frames#

A fault-tolerant machine is half classical, and that half is just as finite as the quantum half: a decoder sitting somewhere, wired to every place that measures, and a memory holding what it decides. Both are places on the same map, and the bits between them are drawn dashed and dark, because a bit is not an ion. The latencies and the decoder profiles come from one table, which the studio and the leaderboard read too.

categoryplacesilhouettemasterops
Decodercontrol roomchip with pinsdec_d3decode
Classical Memoryarchivedrum of cellsarch1record, update, resolve
Classical Data Buswiredashed link—messages

Ports and wires. A Port has a kind: ion or classical. A classical port carries a signal (syndrome, outcome, frame, decision) and a number of bits, has no device node and no stub, and cannot be joined to an ion port — G1 refuses it, and so does the signal type. A Channel has a kind too: a wire has one cost, latency_us, and never enters the road network the scheduler routes ions on. Every measuring place has a syn port, the two that produce a logical result have res, and the three whose operation can be made conditional have ctl.

The control floor. The town puts the decoder and the archive below the last street and wires them up: place.syn → decoder.syn (5 µs), decoder.frame → archive.frame (1 µs), place.res → archive.in (5 µs), archive.ctl → place.ctl (2 µs). The wires run down to a bus line below the town and along it, so they never pretend to be streets.

Decoding is an instruction. A place that measures keeps its syndrome in its readout buffer; nothing reaches the decoder until the program says decode. Then each waiting window -- one op's syndrome, bits wide -- leaves the place that measured it down that place's syn wire, the decoder runs one decode job on it, and the frame update goes down decoder.frame into the archive. decode q0 q1 takes the windows those blocks were measured in, including those of a |Ȳ⟩ block an S̄ gadget consumed into them; a bare decode takes every window still waiting. It cannot be guarded (every syndrome is decoded in every branch) and names no outcome. Two rules make it more than a label:

The decoder works alongside the ions. A decode holds up nothing that does not read what it decides: the next instruction starts when the ions are ready, exactly as if the line were not there ( checks that adding decode lines moves no ion by a microsecond), and only a guard waits for it. The shipped programs decode after the rounds that make their results and at the end, so a makespan grows by the last decode's few microseconds at most (frames: 110.2 → 110.3 ms). On the Design canvas a decode lights exactly the wires it uses, and the places at their ends, while it runs, and the playhead slows through it (§11).

Latencies, each from a measured system: 5 µs from a measurement to a conditional branch on the control processor (AQT M-ACTION, arXiv:2101.11390); 1 µs for one lookup (an FPGA belief-propagation iteration is 24 ns, IBM real-time decoder arXiv:2510.21600, so a d = 3 table read is sub-µs); 0.1 µs to write the archive; 0.2 µs to read it back; 2 µs for a guard to reach the place that waits for it. The reaction time — last measurement to decoded result, the quantity Walking Cat (arXiv:2604.19481) budgets a syndrome round for — is 6.1 µs here when the program decodes straight after the round, against milliseconds per place op, so the classical half costs these schedules nothing. That is the least it can be: a program that decodes later has chosen to wait, and the decode's inspector shows the wait per window. A message leaves no earlier than its bits exist (the op's last measurement, read from the replayed instruction times); a syndrome then waits in its place until a decode sends it.

The decoder is verified, not asserted. The lookup table builds its function from the syndrome-extraction gadget's own fault experiment (§9.2): group every single fault by the detector signature it produces, and read off the observables it flips. The table is a function exactly when no two faults with one signature differ in their logical effect, and the all-zero signature must flip nothing. For se.1 that is a 16-bit, 136-entry table that corrects every one of the 400 single faults. Its op's status is modeled, the reporting contract's word for a number from a stated model rather than this repository's verifier: a decoder has no ions, so there is no TSIR to replay.

Frames: what "kept in software" means. Every block carries a Pauli frame X̄^ax Z̄^az whose two bits are parity expressions over logical outcomes — a constant and a set of (event, flow) names, resolved to records by the sign-off. The archive holds them, and The scheduler propagates them:

The S̄ gadget is the first gadget built by composing places rather than by building a device: a |Ȳ⟩ block from the pharmacy (inject.Y: the corner ion is reset, H and S here, then encoded — verified complete, distance 1 like any injection), a Z̄Z̄ lattice surgery against the data block, an X̄ readout of the |Ȳ⟩ block, and a Z̄ frame in the archive conditioned on the parity of the two outcomes (Litinski 2019, Game of Surface Codes, §4: a π/4 rotation is a Z̄Z̄ measurement against |Y⟩). s_gate.alg does it twice, which must make a Z̄: the sign-off confirms X̄[q0] = 1 deterministically, which is a check on the correction's condition, not an assumption about it.

Classically controlled execution. if <condition>: <instruction> is a dynamic circuit: the ions travel and the place reserves the op either way -- the worst case, so the schedule stays static -- and only the pulses depend on a bit measured while the machine runs. That is a pre-compiled branch with the same port contract, which is how trapped-ion control systems run conditionals (AQT M-ACTION, arXiv:2101.11390), and it is what OpenQASM 3's if and the QIR adaptive profile express at the language level.

A condition is exact and linear: the parity of a constant and some measured outcomes, c ⊕ m₁ ⊕ m₂ …, written m, !m, m == 0, m ^ k. The archive resolves it once the outcomes are recorded and sends it down the decision wire to the place's ctl port; the op may not start before it lands, which G11 checks — and a guard on an instruction that no place can be told about (syndrome extraction has no ctl port) is refused with that reason. A guarded instruction may not name a new outcome, so nothing downstream depends on whether it ran.

Per-branch sign-off. A dynamic program means something different for each value of the bits its guards read, so the flattening signs it off once per branch: the events of a guarded instruction that does not run are left out (its place ran the skip branch — ions in, wait, ions out), the ideal circuit is built for that branch, the archive's lines are folded in the order they were written with the guarded ones kept only where they hold, and the branch's own bits are substituted rather than compared. That last step is what makes the statement readable: m ⊕ r = 1 in the branch where the correction ran is the claim r = 0. A branch the algorithm can never take (a guard reading an outcome the program fixes) is reported as unreachable instead of being checked vacuously.

The archive's lines are operations on a frame, not snapshots: X̄^dax Z̄^daz, optionally after conjugating through S̄ (X̄ → Ȳ) or copying another block's frame (a transversal CNOT). So a conditional S̄ and a conditional Pauli on one block compose, and a block may be corrected and then measured -- the readout is corrected by whatever frame that branch has. The one thing refused is a bit a guard reads that would itself depend on a branch.

What the three examples show (§9.3):

what is conditionalwhy it has to be
cond_cxa transversal CNOT at the workshopthe plainest form: the same ions, the same slot, the pulses gated on one bit. The two branches promise different things (u = 0 versus u ⊕ v = 0) and both are checked
t_gatethe S̄ gadget, and a Paulia T teleportation's S̄ correction is Clifford, not Pauli: no frame can hold it, so the gadget itself must run, and only when the surgery outcome says so
teleport_fixthe Pauli correction after a Bell measurementthe receiving block ends in a known state: r = 0 in every branch, instead of r ⊕ m₂ = 0 for the rest of the program to carry

The T gadget's flows are Clifford statements, so they show that the hardware runs exactly the conditional Clifford program stated -- teleportation, plus S̄ exactly in the branches where the surgery outcome asks for it. That the program is the right one for a T gate is a statement about a non-Clifford state, and it is checked on the state itself, while the T-ness of the resource comes from the factory's own state-vector check (§9.2).

The new checks. G10: every message crosses a wire the design has, takes that wire's latency, leaves no earlier than the op that measured its bits began (and, unless it is a syndrome a decode sent from the readout buffer, no later than that op's end), and is used by something that runs after it arrives; no frame or outcome names a measurement that has not happened. G11: every syndrome window is decoded, by a decode whose message carries it from the place that measured it; no decoding job waits past the next syndrome's arrival (the backlog problem); no classically controlled op starts before its guard has reached it; and no guard, nor any guarded frame write, reads an outcome before the decoder has corrected it.

Where the loop shows up outside this section. The same numbers are the site's, from one table (LINK, DECODERS, cycle_report, and the cycle analysis, which is sweepable — how slow may my decoder be before the backlog grows):

Not established here: a decoder for more than one window at a time (the table is per-round), soft-information decoding, the cost of the classical hardware itself, and route-changing branches -- a guard can gate an op, not send a block somewhere else, and bounded retries (a factory rejection) still need the worst-case reservation of §14's P13. A guard also cannot yet read a bit that itself depends on a branch, and a software H̄ (a Clifford frame rather than a Pauli one) is not implemented.


11 · The design tool#

index.html in the build directory, plus leaf/<master>.js (one master's device and programs, loaded with a plain <script> so file:// works) and, with --studio, studio/<master>.<op>.html. Canvas throughout; no network.

One visual language with the studio. A trapping site, a junction, a rail and an ion are drawn here by the same numbers studio.html draws them by: the page is handed the studio's own PALETTE and GEOMETRY and reproduces its shapes — a site is a capsule of length min(0.88g, (0.30 + 0.15m)g) and thickness 0.1992g rotated onto its trap axis, with one ring per slot and its stroke saying what it is (over capacity, in play, a dock at degree ≥ 3, a corner, or just its zone); a junction is a sharp white square of side 0.60g; a rail is a butt-capped line coloured by its role (rail, highway, compute thin); an ion is a white-outlined disc of radius r_ion when it is in play and r_rest otherwise, with the gold halo under a site in play and the potential-well ellipse under an ion in flight. g — the nearest-neighbour distance — is measured per device and multiplied by the camera's scale, which is what the studio's fit does. Ion colour follows the studio's rule (what the machine is doing to this ion now, not what role a code gave it): a gate teal, a measurement or reset red, everything else slate. Change the palette in one place and both tools move together.


12 · Measured#

small (--rows 1 --pairs 2)showcase (8 × 6)
code blocks / logical qubits4 / 4896 / 1,152
ions at rest4159,720
instructions (T gates)13 (1)335 (24)
events / carries95 / 643,456 / 2,626
machine time (makespan)456.6 ms995.6 ms
G1–G9all passall pass
synthesis + checks (warm cache)0.2 s0.6 s
characterizing all leaves (cold cache)≈ 15 ssame leaves
page + leaf data0.23 MB + 2.2 MB1.7 MB + 2.2 MB
page ready in headless Chrome (file://)—0.83 s
one frame at the top level, every leaf's ions replayed2.5 ms10.9 ms
JS heap—21 MB

A frame costs what it draws. The 10.9 ms above is a 1200 × 800 window; the same page, same build, measured across three windows: 11.2 ms at 1200 × 800, 15.7 at 1400 × 900, 47.9 at 1700 × 1050 (best of 40 calls, showcase, 9,720 ions). So quote the window with the number or the number means nothing -- a figure remembered without its window looks like a 4x regression the next time somebody measures on a bigger screen. It is not one: that is what this note exists to say.

Tests, 120 of them and about four minutes cold: the front end (23), the schedule (25, including a mutation for every G-rule), the page (3: its replay of every leaf program in the browser ends on exactly the verifier's positions), the browser (7: headless Chrome, every level, the editor round trip, a verified algorithm page, all thirteen silhouettes), the logic (12), the places (10), the algorithms (18), and the classical half (22: the decoder's table, a wire that refuses ions, causality and backlog, the frames the archive holds, the S̄ gadget, guards and their delivery, a sign-off per branch, and the mutations of a conditional correction that must fail).


13 · Formats#


14 · Phases#

deliverablestate
P0this plan; model, codes, LogicQ front end✅ k and bases match the Tutorial; every accepted form parses
P1leaves mem, tcx, res, xjunc, channel pricing; characterization with cache✅ every op verified
P2composites, synthesis, GIR, G1–G9, CLI✅ the 9,720-ion showcase passes every G-rule
P3the page: run, descend, leaf replay, library, follow an ion✅ headless walk, JS/Python replay parity
P4edit: place, move, connect, group, ungroup, export, --design✅ an edited design round-trips and synthesizes
P5fac_t15 and the magic path✅; PPM gadgets for single logicals, mixed-basis PPMs, OpenQASM input: open
P6flat sign-off on a small design✅ as logic (§9.3: every example algorithm); the flat 27-rule replay across instance devices: open
P7verified place library: categories and silhouettes, logic checks, fault distance✅ §9.1–9.2, every op verified, distances cross-checked with stim
P8Logical Algorithm: the town, the scheduler, place-model G1–G9, end-to-end sign-off✅ §9.3, eight algorithms; a composer for new towns in the page: the editor, not yet the scheduler
P9the classical half: classical ports, wires, messages, the control floor, G10/G11✅ §10; drawn dashed with packets, causality and backlog checked
P10the decoder as a gadget: a lookup table verified against the SE gadget's own faults, stated latencies, reaction time✅ §10; 136 entries, every single fault corrected
P11corrections kept in software: the classical memory, the frame algebra, the S̄ gadget✅ §10; frames.alg, s_gate.alg; the sign-off allows exactly the archive's frames
P12classically controlled execution: guards on ops (if (m) …), pre-compiled branches with identical port contracts, per-branch sign-off✅ §10; cond_cx, t_gate, teleport_fix; mutate a guard or a correction and the sign-off fails
P13route-changing branches: bounded retries (factory rejection), worst-case reservationopen

15 · Open questions and findings#