The machine
Everything on this website happens on a machine: a programme runs on one, a rule is an assertion about one, the compiler plans on one, an evaluation prices one. This page defines that object. A machine is a text: a document with a fixed grammar, read by one loader into one graph with tables attached, and every other page reads that graph and nothing else. The same text is also a programme, a list of statements that rebuilds the machine, which is how the Design page lets you edit one.
On this page
The Physics page said what a trapped-ion machine can do and named the five words its map is drawn in: site, segment, junction, loop, zone type. This page makes those words formal, so that the next three pages can speak about one well-defined object.
- One text, two readings. A machine is stored as a document and shown as the programme that rebuilds it; both denote one object, the expanded graph with its tables, and the site checks that they do.
- The abstract syntax. What a machine document may say, as a grammar: geometry (nodes, rails, loops), zone types, primitive curves, control, heating, species, budget. Each production is defined once and shown on a real machine.
- Well-formed machines. Which documents are machines at all: ten checks, each quoted with the words the loader prints when it fails.
- What the text derives. Degree, junction, bend, corner, slot, shift map and hop are not written in the document; they are computed from it, and they are what the rules, the compiler and the proved checker read.
- What a machine means. The state space a programme steps, the alphabet its statements must be written in, and the price list its replay reads; then who reads which part of the text: the language, the rules, the compiler, the evaluation, the proved checker.
- Six machines read as texts, each playable; the generators that write a machine from a few numbers; and how to write one by hand, in the Design page or in Python.
A programme says p.move("d0", "S0", "A0"). What has to exist before that line means anything?
A machine: a text that says S0 and A0 are sites, that one rail joins them, how many ions each holds, what crossing that rail costs in time and heat, and which movement class may carry an ion along it. A programme is written in the names a machine declares, a rule quantifies over the sets a machine defines, and the compiler searches the graph a machine draws. So the machine comes first, and it has to be defined as carefully as the programme.
- One text, two readings
- The abstract syntax
- Well-formed machines
- What the text derives: degree, junction, bend, slot, hop
- What a machine means
- Who reads what
- Six machines, read as texts
- Generators: a machine from a few numbers
- Writing a machine
- Conclusion
1. One text, two readings
Is a machine a file, a picture, or a program?
All three name one thing. The file is the document; the picture is drawn from the graph the document loads into; and that graph can be printed back as a programme whose statements, run in order, rebuild it. The graph is the machine. The other two are its syntax.
One word first. Every page of this website, and the Studio, says machine for this object. The file format and the code keep an older spelling in their identifiers (a .arch.json document's geometry, the Python class that holds it), and the architecture description reference uses that spelling where it names those identifiers; it is the same object, the M of this page.
Why two forms? A document is what a file should be: data, with a schema, that a loader validates and nobody has to execute. A listing is what an editor should show: statements a person can read top to bottom, change one of, and run again, which is what the Design page's text lane does and what a Python session does. A machine written in either form is the same machine, and the page holds itself to that: for every machine shown here the listing is run again and the result compared with the original when the site is built.
ring6d, as a document and as a listing. The document's geometry is compact, a generator and its parameters; loading it expands the generator into 8 nodes, 8 rails and 1 loop. The listing's first statement is the same generator call; the statements after it declare the zones, the movement classes and the prices, in that order, because a zone must exist before a site is placed in it and a class must exist before a cycle uses it.# -- geometry --------------------------------------------------------------------
m = Machine.blank("ring", width=3, verticals=2, name="ring6d", zones=["data", "trap", "load"])
# generator defaults left implicit: height=2, site_zone="data", ancilla_zone="trap", segment_capacity=1, loop_id="L0", dock_offset=0
# -- zones: capacity (R1) and what may happen there (R6) -------------------------
m.set_zone("data", capacity=2, gate=False, spam=False, cool=True)
m.set_zone("trap", capacity=2, gate=True, spam=True, cool=True, note="general-purpose trap: gate, measure and cool all available")
m.set_zone("load", capacity=8, gate=False, spam=True, cool=True, photoionization=True)
# no per-site capacity overrides: every site inherits its zoneThe drawing beside it is read off the same graph: six loop sites, two docks, the two degree-3 nodes where a spur meets the loop ringed dark.
So a machine is one object with two spellings. The next section writes down what the object may contain, production by production.
2. The abstract syntax
What is the smallest set of things a machine must declare for a programme to be checkable on it?
Where ions can stand and how they are connected (the geometry), how many fit and what may happen to them there (the zones), what each move costs (the primitives), and what may move together (the control). Everything else, heating, species, budget, refines the price or bounds the hardware.
machine ::= (name, geometry, zone_types, primitives, control, heating, species, budget)
geometry ::= generator(params) -- compact: a function that writes the explicit form
| explicit(nodes, segments, loops) -- expanded: every node, rail and loop stated
node ::= site(id, pos, zone_type, capacity?) -- a place an ion can stand
| junction(id, pos) -- a bare meeting point; it holds no ion
segment ::= rail(id, a, b, length?, capacity?, loop?) -- a and b are node ids; capacity defaults to 1
loop ::= loop(id, <n1, ..., nk>, closed, kind) -- k >= 2; kind is ring or path
zone_types ::= { zone ↦ (capacity, gate?, spam?, cool?, photoionization?) }
primitives ::= { name ↦ curve | curve_by_degree | scalars }
curve ::= [ point, ... ] -- at least one operating point
point ::= (us, quanta, table?, source?, label?)
control ::= (model, classes, max_simd_classes_per_cycle, intra_inter_exclusive, wiring, channels, optical)
class ::= (id, type, orbit, delta?, direction?, entails?)
heating ::= (anomalous_rate_quanta_per_ms, ...)
species ::= (qubit, coolant, sympathetic, coolant_fraction, T_coh_s, ...)
budget ::= { max_dacs, max_junctions, max_junction_degree, min_rail_angle_deg, ... }
pos ::= (x, y) id ::= a name zone ::= a name declared in zone_typesRead it as a type: a machine is a record of eight fields; the geometry is a sum type, compact or explicit, and the loader always produces the explicit form. A question mark marks a field with a default. The concrete syntax is JSON, with these keys spelled exactly as written (zone_types, max_simd_classes_per_cycle); the architecture description reference has every key and default.
In words. A machine is the whole document, not its drawing. The drawing is G; the rest is what the drawing costs, what it allows, and how it is driven.
In words. The geometry is a graph with named paths drawn on it. Nothing in it says junction in the sense the rules use; that is derived (M10).
In words. Two kinds of node, and the kind is declared. Whether a site acts as a junction (an ion pays to cross it) is not declared; it follows from how many rails meet there.
In words. A rail is the stretch of trap axis an ion is shuttled along. Its capacity is how many ions may be on it in one cycle, which is almost always one.
In words. A loop is a named path. Keeping it in the geometry, instead of rediscovering cycles when compiling, is what lets p.rotate be one statement and one movement class.
chain4 written explicitly: four sites in a line, three rails, one open loop. The generator line writes exactly this when it is expanded; the fields degree and corner are cached derived values (M10, M11), re-checked on load."geometry": {
"nodes": [ // C2, C3 alike
{
"id": "C0",
"pos": [
0.0,
0.0
],
"kind": "site",
"capacity": 2,
"zone_type": "trap",
"degree": 1,
"corner": false
},
{
"id": "C1",
"pos": [
1.0,
0.0
],
"kind": "site",
"capacity": 2,
"zone_type": "trap",
"degree": 2,
"corner": false
}
],
"segments": [ // E1, E2 alike
{
"id": "E0",
"ends": [
"C0",
"C1"
],
"length": 1.0,
"capacity": 1,
"loop": "P0"
}
],
"loops": [
{
"id": "P0",
"kind": "path",
"nodes": [ // C2, C3 alike
"C0",
"C1",
"C2",
"C3"
],
"closed": false,
"note": "open register: no rigid rotation, so no single movement template"
}
]
}In words. A zone type is a reusable label: declare trap once as “two ions, gates, measurement and cooling allowed”, then stamp it on a hundred sites. Capacity is rule R1's bound, the flags are rule R6's permissions, and motion is independent of both: an ion may cross a site it cannot be gated in.
ring6d the loop sites are zone data and the two docks zone trap; the third zone, load, is declared and unused. So cap(S1) = 2, can(S1, gate) is false, can(A0, gate) is true.# -- zones: capacity (R1) and what may happen there (R6) -------------------------
m.set_zone("data", capacity=2, gate=False, spam=False, cool=True)
m.set_zone("trap", capacity=2, gate=True, spam=True, cool=True, note="general-purpose trap: gate, measure and cool all available")
m.set_zone("load", capacity=8, gate=False, spam=True, cool=True, photoionization=True)
# no per-site capacity overrides: every site inherits its zoneIn words. A machine does not have a cost per move; it has a menu per move, because slowing a transport buys coldness, and the compiler is allowed to make that trade. A policy is the choice of one row from each menu.
m.set_curve("shuttle_segment", [
dict(us=5.0, quanta=0.1, table="qccdsim_jones", source="2510.23519", label="t7 ion shuttling, one segment"),
dict(us=10.0, quanta=0.1, table="cyclone", source="2511.15910", label="move, one shuttling zone; quanta borrowed from Jones"),
dict(us=12.0, quanta=1.0, table="transport_excitation", source="2605.25118", label="single-ion transport, fast operating point"),
dict(us=14.0, quanta=0.1, table="transport_excitation", source="2605.25118", label="single-ion transport, slow operating point")
])In words. This is where broadcast lives. One cycle, one waveform (rule R22): every ion that moves in a cycle moves the same way, and a site may only opt out. The classes are the ways there are to move; the cycle bound says how many at once.
ring6d declares: the two rotations of L0, the dock and undock along the spurs, two sorting shifts, and the plain shuttle; one class per cycle.m.declare_class("rotate_cw", type="shift", orbit="L0", delta=1)
m.declare_class("rotate_ccw", type="shift", orbit="L0", delta=-1)
m.declare_class("dock", type="shift", orbit="spurs", direction="inward", entails=["split", "merge"])
m.declare_class("undock", type="shift", orbit="spurs", direction="outward", entails=["split", "merge"])
m.declare_class("sort_merge", type="shift", orbit="L0", delta=-1, entails=["merge"])
m.declare_class("sort_split", type="shift", orbit="L0", delta=1, entails=["split"])
m.declare_class("shuttle", type="shift", orbit="any")
m.set_control(max_simd_classes_per_cycle=1)That is the whole syntax: a graph with named paths, a zone table, a price table, a control plane, three small records. Not every text in this grammar is a machine. The next section says which are.
3. Well-formed machines
A document can name a rail to a node that does not exist, or a loop of one node. Who refuses it, and when?
The loader, before anything else runs. A machine is loaded once; if any check below fails, there is no machine, so no programme can be written on it and no rule can be asked about it. The checks are structural: they read the document and nothing else.
In words. Ten sentences, and a hand-edited file cannot quietly disagree with its own graph. The last check is what makes the cached fields safe to read.
| the check | what was written | the loader says |
|---|---|---|
| every end of a rail is a node | d.segment("E3", "C3", "C4") # there is no C4 | segment 'E3': unknown endpoint 'C4' |
| a rail joins two different nodes | d.segment("E3", "C3", "C3") | segment 'E3': self-loop is not a transport segment |
| one rail at most between two nodes | d.segment("E9", "C1", "C0") # E0 already joins them | segment 'E9' duplicates 'E0' between ['C0', 'C1'] -- parallel segments are not modelled |
| a loop has at least two nodes | d.loop("P1", ["C0"], closed=False) | loop 'P1': has 1 node(s); a transport loop needs at least 2, because a shorter one has no segment to walk and no .arch.json can hold it |
| a loop visits a node once | d.loop("P1", ["C0", "C1", "C0", "C2"], closed=False) | loop 'P1': repeats a node |
| consecutive nodes of a loop are joined by a rail | d.loop("P1", ["C0", "C2"], closed=False) | loop 'P1': "no segment between 'C0' and 'C2'" |
| a site holds at least one ion | a site C0 with capacity 0 | node 'C0': a site needs capacity >= 1 |
| a bare junction holds none | a junction J with capacity 2 | node 'J': a bare junction holds no ions, so capacity must be 0 |
| every zone a site names is declared | d.site("C0", 0.0, 0.0, zone="storage") | node 'C0': no zone type 'storage' is declared (have: data, load, trap) |
| a cached derived field agrees with the graph | "degree": 10 written on C1 | node 'C1': file says degree 10, graph gives 2 |
Three more checks read the positions rather than the graph, and they are rules of the rule language, so that a machine model may set their numbers: R19 bounds the degree of a node (six unless the document says fewer; a planar chip says four), R20 requires two rails meeting at a node to subtend at least a minimum angle (60 degrees by default), and R21 requires rails to be planar, meeting only the nodes they end at and crossing only at a shared node. A Tanner graph laid out as rails, or a torus drawn flat with its wrap wires across the die, is a well-formed document and not a surface trap; these three say so, from the drawing alone. The Rules page shows each with a machine that keeps it and one that breaks it.
A well-formed machine is a graph with tables. Most of what the other pages talk about, junctions, corners, slots, hops, is in none of those tables. It is derived, as the next section defines.
4. What the text derives: degree, junction, bend, slot, hop
The document never says “this node is a junction”. How does the rule that prices a junction crossing know where one is?
By counting. A junction is a node where three or more rails meet, and the price is read at that count. Attach a spur to a loop site and it becomes a junction with no word changed; the cost model finds out by counting, not by being told.
In words. Junctions are found, not declared. That is why a dock spur is expensive: it makes the loop site it meets a degree-3 node, and every rotation through that site now pays a junction crossing.
In words. A rail that turns is still one rail to the ion. On a ring built with junction corners, one slot of rotation carries an ion from the last trap before the corner, round it, to the first trap after; nothing is charged for the turn, because there is no barrier in a continuous RF null.
In words. The shift map is the conveyor belt. Its domain is the slots, so a bend is passed and never landed on, and its size is the loop's length, so turning a loop of 144 ions is one instruction.
In words. An ion leaves a trap, crosses junctions, lands on a trap, all in one cycle: that is a hop. The compiler plans in hops, the proved checker is handed the hop set read off the document, and a programme that asks for a pair not in it is refused before any rule is consulted (next section).
| machine | degrees | junctions (degree ≥ 3) | corners of its loops | bends (degree-2 turns) | loops and their slots |
|---|---|---|---|---|---|
| the register (chain4) | 2 of degree 1, 2 of degree 2 | ∅ | ∅ | ∅ | P0 (open, 4 slots) |
| the racetrack (race8) | 8 of degree 2 | ∅ | S0, S3, S4, S7 | S0, S3, S4, S7 | L0 (closed, 8 slots) |
| the lattice (grid2x3) | 11 of degree 2, 2 of degree 3 | J0_1, J1_1 | J0_0, J0_2, J1_0, J1_2 | J0_0, J0_2, J1_0, J1_2 | none |
| the larger lattice (grid3x3) | 16 of degree 2, 4 of degree 3, 1 of degree 4 | J0_1, J1_0, J1_1, J1_2, J2_1 | J0_0, J0_2, J2_0, J2_2 | J0_0, J0_2, J2_0, J2_2 | none |
| the two loops (dual3) | 10 of degree 2, 2 of degree 3 | AT1, DT1 | AB0, AB2, AT0, AT2, DB0, … (8) | AB0, AB2, AT0, AT2, DB0, … (8) | D (closed, 6 slots); A (closed, 6 slots) |
| the ring with docks (ring6d) | 2 of degree 1, 4 of degree 2, 2 of degree 3 | S0, S3 | S0, S2, S3, S5 | S2, S5 | L0 (closed, 6 slots) |
With the derived notions in hand, the question of meaning can be asked precisely: what does a machine determine about the programmes written on it?
5. What a machine means
The Language page says a programme steps a state of three parts. Where does that state live?
In the machine. The positions are a map into M's sites, the moves are along M's rails and loops, the prices are M's curves. Change the machine and the same programme text means a different run, or none.
p.init fills the map. A state is admissible when it respects M: |pos⁻¹(n)| ≤ cap(n) for every site, and no ion stands on a bare junction.In words. Three things: where, how hot, what time. The machine supplies the set of places and the limits; the programme supplies the moves.
The state after p.init({"d0": "C1", "d1": "C2"}) and one hop on the register: a position for each ion, a heat for each, a clock. Every statement of the hardware language is a step ΣM → ΣM; the Language page gives the step of each.
init names sites; a move from a to b requires (a, b) ∈ hops(M) and a declared class; a rotate names a closed loop of L; a gate on a site requires can(site, gate). A programme is well-typed when every statement is. Well-typed is not legal: a well-typed programme is then replayed and every cycle judged by the rules; an ill-typed one is refused by the replay itself, with the name it could not find.In words. Typing is the machine's part of the bargain, rules are the physics' part. “No rail between S0 and S3” is a type error, “two ions on a rail of capacity one” is a rule failing (R3).
ring6d, p.move("d0", "S0", "S1") is well-typed: S0 and S1 are sites and one rail joins them. p.move("d0", "S0", "S3") is not: no rail joins them, and the replay stops with no segment between 'S0' and 'S3' before any rule is asked. p.gate("CX", [["d0", "d1"]], sites=["S1"]) is well-typed (S1 is a site) and illegal (can(S1, gate) is false), which rule R6 reports with the site's name.What the replay reads from M, step by step: for a transport cycle, the rails crossed give the primitives used (a shuttle per rail, a junction_cross at the degree of every junction transited, a split and a merge when the class entails them), and the policy picks a point of each curve, so the cycle's duration and heat are sums of operating points; for a gate, the gate primitive's duration, and the error read against each ion's n̄; for cooling, the cooling primitive. The Evaluation page is the catalogue of the numbers so obtained.
6. Who reads what
| reader | what it reads of M | what it calls it | where |
|---|---|---|---|
| Language | sites, hops, closed loops, declared classes, abilities | the names a statement may use (M15) | Language · semantics |
| Rules | sites, junctions, rails, capacity, degree, zone, can, ends, rails_at, loops, the declared numbers | the machine vocabulary: the Hardware record | Rules · the words |
| Compiler | traps T, junctions J, rails E, cap, gate, meas, closed loops L; the curves, for cooling insertion | the trap graph A(M), Definition 1 there | Compilation · 5.1 |
| Evaluation | the curves under a policy, the wiring and budget, the heating rate, the species | the machine's tables, the machine's wiring, the machine's laws | Evaluation · the words |
| Proved checker | gate-capable sites, hops(M), each closed loop's slots | gateSites, hops, loops: facts the compiler cannot widen | Compilation · 5.9 |
ring6d, read when the site was built.The Hardware record the rules read (field, meaning, value here):
| field | meaning | on ring6d |
|---|---|---|
| sites | every place an ion may stand | S0, S1, S2, S3, S4, S5, A0, A3 |
| junctions | nodes where three or more rails meet | S0, S3 |
| rails | the segments, with ends(r) | 8 rails |
| capacity(x) | ions a site or rail holds at once | 2 in zone data, 2 in zone trap; 1 on every rail |
| degree(n) | rails at node n | S0: 3, S1: 2, S2: 2, S3: 3, S4: 2, S5: 2 … |
| zone(s) | the zone type of site s | data, trap |
| can(s, a) | gate / spam / cool at site s | gate: A0, A3; spam: A0, A3 |
| loops | each loop's slots in order | L0 = ⟨S0, S1, S2, S3, S4, S5⟩ |
| numbers | the declared limits | max_junction_degree = 6, min_rail_angle_deg = 60.0, max_simd_classes = 1, control_model = simd_classes |
The trap graph the compiler reads: T = {S0, S1, S2, S3, S4, S5, A0, A3}; J = {}; |E| = 8; cap = 2; gate = {A0, A3}; meas = {A0, A3}; L = {L0 = ⟨S0, S1, S2, S3, S4, S5⟩}.
What the proved checker is handed: gateSites = {A0, A3}; hops = 28 ordered pairs, e.g. (S0, S1), (S0, S5), (S0, A0), (S1, S0) …; loops = L0 ↦ ⟨S0, S1, S2, S3, S4, S5⟩.
Five readers, one text. The next section reads six machines as texts, each in the four forms of section 1, and each playable.
7. Six machines, read as texts
the register: chain4
a line of four traps: no junction, no closed loop.
m = Machine.blank("chain", n=4, name="chain4", zones=["data", "trap", "load"])
# generator defaults left implicit: site_zone="trap", segment_capacity=1, path_id="P0"Expands to 4 nodes (4 sites, total capacity 8), 3 rails, 1 loop; junctions ∅; corners ∅.
p.init({"d0": "C0", "d1": "C2"})
p.move("d0", "C0", "C1")
p.move("d1", "C2", "C1")
p.cool()
p.gate("CX", [["d0", "d1"]])the racetrack: race8
one closed loop of eight traps: rotation, no junction.
m = Machine.blank("racetrack", straight=4, name="race8", zones=["data", "trap", "load"])
# generator defaults left implicit: site_zone="trap", segment_capacity=1, loop_id="L0"Expands to 8 nodes (8 sites, total capacity 16), 8 rails, 1 loop; junctions ∅; corners S0, S3, S4, S7.
p.fill()
p.rotate(2)the lattice: grid2x3
junctions of degree 3 beside plain corners, a trap on every wire.
m = Machine.blank("grid", a=2, b=3, name="grid2x3", zones=["data", "trap", "load"])
# generator defaults left implicit: site_zone="trap", segment_capacity=1, spacing=1, periodic=FalseExpands to 13 nodes (7 sites, total capacity 14), 14 rails, 0 loops; junctions J0_1, J1_1; corners J0_0, J0_2, J1_0, J1_2.
p.init({"d0": "T0_0v"})
p.shuttle("d0", ["T0_0v", "J0_1", "T0_1h"])the larger lattice: grid3x3
an X-junction of degree 4 in the middle.
m = Machine.blank("grid", a=3, b=3, name="grid3x3", zones=["data", "trap", "load"])
# generator defaults left implicit: site_zone="trap", segment_capacity=1, spacing=1, periodic=FalseExpands to 21 nodes (12 sites, total capacity 24), 24 rails, 0 loops; junctions J0_1, J1_0, J1_1, J1_2, J2_1; corners J0_0, J0_2, J2_0, J2_2.
p.init({"d0": "T0_1h"})
p.shuttle("d0", ["T0_1h", "J1_1", "T1_1v"])the two loops: dual3
two closed loops that share no site, joined where two rails meet two.
m = Machine.blank("dual_loop", width=3, couplings=[1], name="dual3", zones=["data", "trap", "load"])
# generator defaults left implicit: data_zone="data", ancilla_zone="trap", segment_capacity=1Expands to 12 nodes (12 sites, total capacity 24), 13 rails, 2 loops; junctions AT1, DT1; corners AB0, AB2, AT0, AT2, DB0, … (8).
p.init({"d0": "DT0", "d1": "AT0"})
p.rotate(1, loop="D")
p.rotate(-1, loop="A")the ring with docks: ring6d
a loop that cannot gate, two spurs to traps that can.
m = Machine.blank("ring", width=3, verticals=2, name="ring6d", zones=["data", "trap", "load"])
# generator defaults left implicit: height=2, site_zone="data", ancilla_zone="trap", segment_capacity=1, loop_id="L0", dock_offset=0Expands to 8 nodes (8 sites, total capacity 16), 8 rails, 1 loop; junctions S0, S3; corners S0, S2, S3, S5.
p.init({"d0": "S1", "d1": "S4"})
p.rotate(-1)
p.move("d0", "S0", "A0", cls="dock")
p.move("d1", "S3", "A3", cls="dock")Five of the six were written by a generator from two or three numbers. The next section says what a generator is, and why the compact form is only kept when it is honest.
8. Generators: a machine from a few numbers
If a ring of 144 slots and 24 docks is stored as two numbers, what happens when someone drags one site?
The two numbers stop describing it. So a document stores its generator and parameters only while re-running them reproduces the graph exactly; the moment it does not, the file is written in the explicit form. Nothing downstream ever reads the parameters: every reader takes the expanded graph.
In words. A generator is a recipe; the graph is the dish. The file keeps the recipe only while it still makes this dish.
| generator | parameters | what it writes |
|---|---|---|
ring | width, height, verticals, dock_offset, corners | one closed loop of 2W + 2H − 4 slots (W, H ≥ 2), V spurs to dock sites, the first at slot dock_offset; with corners = "junction", four bends at the corners and traps only on the sides |
racetrack | straight | one closed loop of 2·straight trap sites and no junction: every bend is ordinary rail |
grid | a, b | an a × b lattice of nodes with a trap in the middle of every wire: interior nodes degree 4, edge nodes 3, corners 2 |
chain | n | n sites in a line, one open loop, two ends of degree 1 |
ladder | width, rungs, highways | two rails joined by rungs |
dual_loop | width, couplings, corners | an inner loop and an outer loop, joined by a rail at each coupling, where two sites become junctions |
cylinder | a, b | the planar drawing of a periodic grid: the wrap in one direction, so that R21 holds |
Generators write most machines. The last section is for the ones they cannot: how a machine is written by hand, as the programme the Design page edits.
9. Writing a machine
Why is the editing language a list of method calls rather than a form or a JSON editor?
Because a list of statements can be read top to bottom, run, and diffed. What the Design page shows is exactly what a Python session prints for the same machine and exactly what it executes; copy the panel into a file and it runs. A form cannot say that, and a JSON editor shows data, not the order in which a machine is made.
dict(…), never an operator, an attribute or a call, and the method names are checked against the table below when the text is parsed. A listing is self-contained when it names no template: every zone, curve and class is stated.In words. A listing is a programme whose output is a machine. The site treats the equation as a test: every listing shown on this page was run again and compared with its machine.
listing ::= (statement | comment | blank)*
statement ::= "m" "=" "Machine" "." seed "(" args ")" -- seed: blank | blank_device | from_device | from_template
| "m" "." method "(" args ")" -- one of the twelve setters, or an edit
| "d" "=" "DeviceBuilder" "(" args ")" -- an explicit geometry begins
| "d" "." builder "(" args ")" -- site | junction | segment | loop
args ::= [ arg ("," arg)* [","] ]
arg ::= name "=" value | value
value ::= string | number | "True" | "False" | "None" | "[" values "]" | "{" pairs "}" | "dict" "(" args ")"
-- no operator, no attribute, no arithmetic, no control flow; a logical line continues while a bracket is open| statement | writes | which part of M |
|---|---|---|
m = Machine.blank(gen, …, zones=[…]) | the geometry from a generator, and the zone names it may use | G (compact), dom Z |
d = DeviceBuilder(); d.site(id, x, y, zone=…, capacity=…) | one site | N |
d.junction(id, x, y) | one bare junction | N |
d.segment(id, a, b, loop=…, length=…, capacity=…) | one rail | E |
d.loop(id, [nodes], closed=…, kind=…) | one loop | L |
m = Machine.blank_device(d.build(), …) | seals an explicit geometry into a machine | G (explicit) |
m.set_zone(zone, capacity=…, gate=…, spam=…, cool=…) | one zone type | Z |
m.set_site_capacity(sites, n) | a capacity a site keeps when its zone is retuned | cap |
m.set_curve(primitive, [dict(us=…, quanta=…, table=…), …]) | a transport primitive's operating points | Π |
m.set_degree_curve(primitive, degree, [...]) | a junction crossing's curve at one degree | Π |
m.set_primitive(primitive, us=…, fidelity=…, …) | a gate or readout primitive's scalars | Π |
m.declare_class(id, type=…, orbit=…, delta=…, entails=[…]) | one movement class | C |
m.set_control(model=…, max_simd_classes_per_cycle=…, channels=…) | the control model and its bounds | C |
m.set_wiring(scheme=…, dacs_dynamic=…, electrodes_per_trap=…) | the wiring block | C |
m.set_heating(…), m.set_species(…), m.set_budget(…), m.describe(…) | the three records and the description | H, S, B, name |
m.move_site(site, x, y), m.set_segment_length(…) | edits to a geometry already sealed | N, E |
d = DeviceBuilder("explicit")
# explicit geometry: the generator and its parameters are kept for provenance but no longer reproduce this graph, so every node, segment and loop is stated
d.site("A", 0.0, 0.0, zone="trap")
d.site("B", 2.0, 0.0, zone="trap")
d.site("C", 1.0, 1.0, zone="trap")
d.junction("J", 1.0, 0.0)
d.segment("A-J", "A", "J")
d.segment("J-B", "J", "B")
d.segment("J-C", "J", "C")
m = Machine.blank_device(d.build(), name="tee", zones=["data", "trap", "load"])p.init({"d0": "A", "d1": "C"})
p.shuttle("d0", ["A", "J", "B"])
p.shuttle("d1", ["C", "J", "B"])
p.cool()
p.gate("CX", [["d0", "d1"]])Where to write one. On the Design page: its Parts mode places sites, junctions and rails with the pointer, its Sketch mode turns a drawn shape into sites and rails, and its text lane shows the listing and accepts edits to it, all three writing the same statements. In Python, the same statements on a Machine, then m.save(path) writes the document and m.listing() prints the programme. In a workspace, the agent edits machines through the same statements. Whichever way, the result is a document that the loader checks (section 3) before any programme runs on it.
Conclusion
The machine is the object the rest of the Learn pages had been using without defining. It is a text with a grammar (section 2) and a static semantics (section 3); it determines a graph and a set of derived facts (section 4); it fixes the state space, the alphabet and the price list of every programme written on it (section 5); and it is read by five readers as five projections of one thing (section 6). The same text can be stored as data or shown as a programme, and the two are checked to agree (sections 1 and 9).
Read the next pages with this in hand. On the Language page every statement is a step of ΣM, well-typed only in M's names. On the Rules page every rule quantifies over sets M defines (sites, rails, junctions) and reads numbers M declares (capacity, the geometry limits), beside the sets a run defines (operations, instants). On the Compilation page the compiler's first definition is the trap graph A(M), a projection of M, and its proved checker is handed three facts read off M's document. On the Evaluation page the three texts a number is a function of are M, a programme and an evaluation.
What to remember
- A machine is a text: a document with the grammar of section 2, loaded once into an expanded graph with tables. The same text prints as a listing, a programme of method calls that rebuilds it, and the two are checked to agree.
- A document is a machine only if it passes ten structural checks (section 3); three more rules read the positions. A programme or a rule never sees a document that failed.
- Junctions, bends, corners, slots, the shift map and hops are derived from the geometry, never declared: a junction is a node of degree three or more, a hop is a trap-to-trap path through junctions only.
- A machine fixes the state space a programme steps (positions, heat, clock), the names a statement may use (well-typed), and the prices a replay reads (curves under a policy). Ill-typed is refused by the replay; illegal is a rule failing.
- Five readers, one text: the language, the rules, the compiler, the evaluation and the proved checker each take a projection of the one machine.
The Language page is next: the statements a programme is made of, each a step of the state space this page defined, on the machines this page drew.