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.

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.

1. One text, two readings

In this section the one idea the page rests on: a machine is a text, and the text has two equivalent forms, a document (data) and a listing (a programme), that denote the same object. The object is what everything downstream reads.

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.

the document ring6d.arch.json {"name": "ring6d", "geometry": { "generator": "ring", "params": {"width": 3, ...}}, "zone_types": {...}, "primitives": {...}, ...} data: what the file stores the listing the programme that rebuilds it m = Machine.blank("ring", width=3, verticals=2, ...) m.set_zone("trap", ...) m.declare_class("rotate_cw", ...) m.set_curve("shuttle_segment", [...]) code: statements, run in order the machine M the expanded graph (N, E, L) + zones, curves, control, budget what every reader consumes load to_json exec listing round trip: listing(M) run again gives M, checked for every machine on this page when the site is built
A machine document is loaded into the expanded graph with its tables, which is the machine M every reader consumes; the same M is printed as a listing, a programme in a grammar of a few method calls and no expressions (section 9), and running that listing gives M back. The two texts are interchangeable; the Studio's Design page edits the listing, the files on disk are documents.

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.

Example The ring with two docks, 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 zone

The 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.

S0S1S2S3S4S5A0A3
ring6d, drawn from its expanded graph: sites are discs coloured by zone, a site where three rails meet carries a dark ring, rails are lines

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

In this section the grammar of a machine document, then one definition per production: machine, geometry, node, rail, loop, zone type, primitive, control. Each definition is followed by its instance on a real machine, so no symbol is left abstract.

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_types

Read 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.

Definition M1 machine. A machine is a tuple M = (G, Z, Π, C, H, S, B) of a geometry G (Definition M2), a family of zone types Z (M6), a table of primitives Π (M7), a control plane C (M8), a heating rate H, a species record S and a budget B, together with a name. Only G, Z and Π are required; the others default to empty, and a word that reads an absent field says so (the rules report could not be checked, never holds).

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.

Definition M2 geometry. A geometry is a triple G = (N, E, L): a finite ordered set of nodes N, a finite set of rails E each joining two nodes, and a finite set of loops L, each a sequence of nodes. The order of N is part of the geometry: a listing prints its statements in it, and two documents with the same sets in different orders print differently.

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).

Definition M3 node. A node is either a site site(id, pos, z, c), a place an ion can stand, with a zone type z ∈ dom Z and an optional capacity c ≥ 1 of its own, or a bare junction junction(id, pos), a meeting point of rails that holds no ion and has no zone. pos ∈ ℝ² is geometry, not decoration: three rules read it (R19–R21, below).

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.

Definition M4 rail. A rail (segment) rail(id, a, b, ℓ, c, λ) joins two distinct nodes a, b ∈ N, has a length ℓ > 0 (default 1), a capacity c ≥ 1 of ions in transit at once (default 1, rule R3's bound), and optionally names the loop λ ∈ L it belongs to. Rails are undirected; at most one rail joins a given pair of nodes.

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.

Definition M5 loop. A loop loop(id, ⟨n₁, …, nk⟩, closed, kind) is a sequence of k ≥ 2 distinct nodes in which consecutive nodes are joined by a rail (and the last to the first when closed). A closed loop (kind ring) is the domain of one movement template, the rigid shift (M12); an open loop (kind path) is a register and has no shift.

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.

Example The register 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"
  }
 ]
}
C0C1C2C3
chain4: N = {C0, C1, C2, C3}, E = {E0, E1, E2}, L = {P0 = ⟨C0, C1, C2, C3⟩, open}
Definition M6 zone types and capacity. The zone types Z map each zone name to a record (capacity, gate, spam, cool, photoionization) of one number and four flags. The capacity of a site is its own c when it declares one and its zone's otherwise: cap(n) = c(n) if set, else Z(z(n)).capacity; a bare junction has cap 0. The abilities of a site are its zone's flags: can(n, a) ⇔ Z(z(n)).a for a ∈ {gate, spam, cool}.

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.

Example On 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 zone
Definition M7 primitives. The primitives Π map a primitive's name to its price. A transport primitive (shuttle_segment, split, merge, ion_swap) has a curve: a non-empty finite set of operating points (us, quanta, table, source), each a measured pair of a duration in microseconds and a heat in motional quanta, tagged with the table it was taken from. junction_cross has one curve per degree. A gate or readout primitive (ms_gate, 1q_gate, measure, reset, cool) has scalars: a duration and a fidelity or an error, and for the two-ion gate the heat budget max_quanta it tolerates (rule R7). An operating-point policy π picks one point of each curve; the price of a cycle under π is the sum over the primitives it uses.

In 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.

Example The shuttle curve of the default template, which every machine on this page carries: four operating points from three published tables. A policy such as (qccdsim_jones, fastest) resolves it to 5 µs and 0.1 quanta; (transport_excitation, coolest) to 14 µs and 0.1.
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")
])
Definition M8 control. The control plane C says what may move together. Its model is one of simd_classes, direct or c2lr. Under simd_classes it declares a set of movement classes, each (id, type, orbit, delta, entails): a motion template over the nodes of an orbit (a loop, the spurs, or any), with the primitives it entails (a dock entails a split and a merge; a rigid rotation entails neither), and a bound max_simd_classes_per_cycle on how many classes one cycle may drive. The wiring block (scheme, electrodes per trap and junction, DACs) and the channel map say what the evaluation's hardware metrics count.

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.

Example The classes 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

In this section the static semantics: the checks a document must pass to be a machine, each with the sentence the loader prints when it fails. The sentences below were produced by breaking the register in one place and asking the loader.

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.

Definition M9 well-formed machine. A document is a well-formed machine, written ⊢ M ok, when it has the shape of the grammar and: (1) every end of a rail is a node of N; (2) a rail joins two different nodes; (3) at most one rail joins a pair of nodes; (4) a loop has at least two nodes, (5) visits no node twice and (6) has a rail between each consecutive pair; (7) a site has capacity ≥ 1 and (8) a bare junction capacity 0; (9) every zone a site names is declared in Z; (10) a derived field cached in the file (degree, corner) agrees with the graph. The loader runs all ten on every load, and a programme or a rule only ever sees a machine that passed.

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.

Example The register, broken ten ways, and what the loader says each time. Every message names the offending object, so the line to fix is the line it names.
the checkwhat was writtenthe loader says
every end of a rail is a noded.segment("E3", "C3", "C4") # there is no C4segment 'E3': unknown endpoint 'C4'
a rail joins two different nodesd.segment("E3", "C3", "C3")segment 'E3': self-loop is not a transport segment
one rail at most between two nodesd.segment("E9", "C1", "C0") # E0 already joins themsegment 'E9' duplicates 'E0' between ['C0', 'C1'] -- parallel segments are not modelled
a loop has at least two nodesd.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 onced.loop("P1", ["C0", "C1", "C0", "C2"], closed=False)loop 'P1': repeats a node
consecutive nodes of a loop are joined by a raild.loop("P1", ["C0", "C2"], closed=False)loop 'P1': "no segment between 'C0' and 'C2'"
a site holds at least one iona site C0 with capacity 0node 'C0': a site needs capacity >= 1
a bare junction holds nonea junction J with capacity 2node 'J': a bare junction holds no ions, so capacity must be 0
every zone a site names is declaredd.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 C1node '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

In this section the notions computed from the geometry that every downstream reader uses: degree and junction, bend and corner, slot and shift map, hop. Each is a definition, a picture, and the values on the six machines.

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.

Definition M10 degree and junction. The degree of a node is the number of rails incident to it, deg(n) = |{e ∈ E : n ∈ ends(e)}|. A node is a junction when deg(n) ≥ 3, whatever its kind (rule R18). A site of degree ≥ 3 is a junction an ion may also stand on; a bare junction of any degree is a node no ion stands on.

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.

J0_0deg 2J0_1deg 3J0_2deg 2J1_0deg 2J1_1deg 3J1_2deg 2T0_0hdeg 2T0_1hdeg 2T0_2hdeg 2T0_0vdeg 2T0_1vdeg 2T1_0vdeg 2T1_1vdeg 2
grid2x3: the two T-nodes J0_1 and J1_1 have degree 3 and are junctions; the four corners have degree 2 and are not
S0deg 3S1deg 2S2deg 2S3deg 3S4deg 2S5deg 2A0deg 1A3deg 1
ring6d: S0 and S3 have degree 3 once the spurs are attached; the other loop sites have degree 2
J0_0deg 2J0_1deg 3J0_2deg 2J1_0deg 3J1_1deg 4J1_2deg 3J2_0deg 2J2_1deg 3J2_2deg 2T0_0hdeg 2T0_1hdeg 2T0_2hdeg 2T1_0hdeg 2T1_1hdeg 2T1_2hdeg 2T0_0vdeg 2T0_1vdeg 2T1_0vdeg 2T1_1vdeg 2T2_0vdeg 2T2_1vdeg 2
grid3x3: the centre J1_1 has degree 4, an X-junction; the edge midpoints degree 3
Definition M11 bend and corner. A corner of a loop is a node at which the loop changes direction: for consecutive nodes p, n, q of the loop, the unit vector from p to n differs from that from n to q. A bend is a node of degree exactly 2 at which the rail turns: a corner of a loop, or a degree-2 node whose two rails are not collinear. A bend is not a junction (rule R18): an ion is carried round it at the price of ordinary rail. A bend that is a site (the corners of ring6d) is a slot like any other; a bend that is a bare junction (the corners of a lattice, or of a ring built with corners = "junction") is no slot: no ion stops there.

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.

Definition M12 slots and the shift map. The slots of a loop are its nodes in order, less the bends that are bare junctions: the places an ion stands on the loop. For a closed loop with slots ⟨s₀, …, sk−1⟩ the shift map by δ is shiftδ(si) = s(i+δ) mod k. One rotation statement applies this map to every ion on the loop at once; it is the single movement template rigid rotation needs.

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.

S0S1S2S3S4S5S6S7
race8: the eight slots of L0 and shift₊₁, every ion one slot clockwise in one cycle
S0S1S2S3S4S5A0A3
ring6d: L0 has six slots; S2 and S5 are its degree-2 corners, S0 and S3 its corners that are also junctions
Definition M13 hop. Two sites a, b are one hop apart when there is a path of rails from a to b whose interior nodes are all junctions (bare, or sites of degree ≥ 3), of at most three interior nodes. hops(M) is the set of such ordered pairs. A transport cycle of a programme is one hop per moving ion; a rotation is one shift.

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).

J0_0J0_1J0_2J1_0J1_1J1_2T0_0hT0_1hT0_2hT0_0vT0_1vT1_0vT1_1v
grid2x3: the hops out of T0_1h, each through one T-junction; the lattice corners are degree 2 and are passed as ordinary rail
Example The derived notions on the six machines of this page, read off their documents when the site was built.
machinedegreesjunctions (degree ≥ 3)corners of its loopsbends (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, S7S0, S3, S4, S7L0 (closed, 8 slots)
the lattice (grid2x3)11 of degree 2, 2 of degree 3J0_1, J1_1J0_0, J0_2, J1_0, J1_2J0_0, J0_2, J1_0, J1_2none
the larger lattice (grid3x3)16 of degree 2, 4 of degree 3, 1 of degree 4J0_1, J1_0, J1_1, J1_2, J2_1J0_0, J0_2, J2_0, J2_2J0_0, J0_2, J2_0, J2_2none
the two loops (dual3)10 of degree 2, 2 of degree 3AT1, DT1AB0, 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 3S0, S3S0, S2, S3, S5S2, S5L0 (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

In this section the semantics: a machine fixes the state space a programme steps, the alphabet a programme's statements are written in (so that a statement can be ill-typed on one machine and fine on another), and the price list the replay reads. The Language page's semantics then takes over.

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.

Definition M14 state space. The state space of a machine is ΣM = (Ion ⇀ sites(M)) × (Ion → ℝ≥0) × ℝ≥0: a partial map from ions to sites (where every ion is), a heat n̄ per ion in motional quanta, and the clock. The initial state is empty; 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.

C0C1d0C2d1C3
posd0 ↦ C1, d1 ↦ C2a partial map ions ⇀ sites(M)
n̄d0: 0.10, d1: 0.00a heat per ion, in motional quanta
clock5 µsthe time elapsed

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.

Definition M15 well-typed statement. A statement is well-typed on M when every name in it is one M declares and every relation it asserts holds in M: an 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).

Example On 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

In this section one table: which part of the machine each of the five readers takes, and the name that reader gives its projection. The point is that there is one text and five projections, never five descriptions of the hardware.
the machine Mone text, loaded onceevery reader takes a projectionLanguagea programme's names must be M's:sites, rails, loops,classes, abilitiesRulesthe Hardware record:sites, junctions, rails, capacity, degree,zone, can, loops, numbersCompilerthe trap graph A(M):(V, E, cap, gate,meas, L), and the prices for coolingEvaluationthe tables:curves, wiring, budget,heating, speciesProved checkerread from the document, never the compiler:gateSites, hops,loop slots
One machine, five readers. Each reads a projection of M: the Language its names and relations, the Rules the Hardware record, the compiler the trap graph, the Evaluation the tables, the proved checker three facts read from the document and never from the compiler.
readerwhat it reads of Mwhat it calls itwhere
Languagesites, hops, closed loops, declared classes, abilitiesthe names a statement may use (M15)Language · semantics
Rulessites, junctions, rails, capacity, degree, zone, can, ends, rails_at, loops, the declared numbersthe machine vocabulary: the Hardware recordRules · the words
Compilertraps T, junctions J, rails E, cap, gate, meas, closed loops L; the curves, for cooling insertionthe trap graph A(M), Definition 1 thereCompilation · 5.1
Evaluationthe curves under a policy, the wiring and budget, the heating rate, the speciesthe machine's tables, the machine's wiring, the machine's lawsEvaluation · the words
Proved checkergate-capable sites, hops(M), each closed loop's slotsgateSites, hops, loops: facts the compiler cannot widenCompilation · 5.9
Example The three projections of ring6d, read when the site was built.

The Hardware record the rules read (field, meaning, value here):

fieldmeaningon ring6d
sitesevery place an ion may standS0, S1, S2, S3, S4, S5, A0, A3
junctionsnodes where three or more rails meetS0, S3
railsthe segments, with ends(r)8 rails
capacity(x)ions a site or rail holds at once2 in zone data, 2 in zone trap; 1 on every rail
degree(n)rails at node nS0: 3, S1: 2, S2: 2, S3: 3, S4: 2, S5: 2 …
zone(s)the zone type of site sdata, trap
can(s, a)gate / spam / cool at site sgate: A0, A3; spam: A0, A3
loopseach loop's slots in orderL0 = ⟨S0, S1, S2, S3, S4, S5⟩
numbersthe declared limitsmax_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

In this section the six machines the examples of this website run on, each shown as its drawing, its generator line and what that line expands to, with a short programme on it you can play. Pressing Play opens the machine's Studio page, whose Machine pane is this machine as a programme, line by line.

the register: chain4

C0C1C2C3
a four-site linear register C0–C3: every site a trap (gates, SPAM, cooling; capacity 2), two open ends, no junctions and no loop

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"]])
well-typed and legal on chain4 · cost 3 · steps 5 · 415 µs · peak n̄ 6.12
C0d0C1C2d1C3
Playthe programme on this machine; its Studio page's Machine pane is the machine as a programme
open the page, with its Report pane

the racetrack: race8

S0S1S2S3S4S5S6S7
an eight-site racetrack S0–S7: one closed loop L0 of trap sites (gates, SPAM, cooling; capacity 2) and no junctions, so every bend is ordinary transport

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)
well-typed and legal on race8 · cost 16 · steps 2 · 10 µs · peak n̄ 0.20
S0d0S1d1S2d2S3d3S4d4S5d5S6d6S7d7
Playthe programme on this machine; its Studio page's Machine pane is the machine as a programme
open the page, with its Report pane

the lattice: grid2x3

J0_0J0_1J0_2J1_0J1_1J1_2T0_0hT0_1hT0_2hT0_0vT0_1vT1_0vT1_1v
a 2×3 lattice: six lattice nodes, of which J0_1 and J1_1 are degree-3 T-junctions and the other four degree-2 corners, with one trap site (T…) on every wire between them

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=False

Expands 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"])
well-typed and legal on grid2x3 · cost 3 · steps 2 · 105 µs · peak n̄ 3.21
J0_0J0_1J0_2J1_0J1_1J1_2T0_0hT0_1hT0_2hT0_0vd0T0_1vT1_0vT1_1v
Playthe programme on this machine; its Studio page's Machine pane is the machine as a programme
open the page, with its Report pane

the larger lattice: grid3x3

J0_0J0_1J0_2J1_0J1_1J1_2J2_0J2_1J2_2T0_0hT0_1hT0_2hT1_0hT1_1hT1_2hT0_0vT0_1vT1_0vT1_1vT2_0vT2_1v
a 3×3 lattice of junction nodes: the centre J1_1 is a degree-4 X-junction, the four edge midpoints degree-3 T-junctions and the four corners plain bends, with one trap site (T…) on every wire between them

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=False

Expands 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"])
well-typed and legal on grid3x3 · cost 3 · steps 2 · 125 µs · peak n̄ 3.21
J0_0J0_1J0_2J1_0J1_1J1_2J2_0J2_1J2_2T0_0hT0_1hd0T0_2hT1_0hT1_1hT1_2hT0_0vT0_1vT1_0vT1_1vT2_0vT2_1v
Playthe programme on this machine; its Studio page's Machine pane is the machine as a programme
open the page, with its Report pane

the two loops: dual3

DT0DT1DT2DB2DB1DB0AT0AT1AT2AB2AB1AB0
two concentric loops: an inner data loop D (DT0–DT2, DB0–DB2; capacity 2, no gates) and an outer trap loop A (AT0–AT2, AB0–AB2), joined at x = 1 where DT1 and AT1 are degree-3 junctions

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=1

Expands 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")
well-typed and legal on dual3 · cost 3 · steps 2 · 105 µs · peak n̄ 3.11
DT0d0DT1DT2DB2DB1DB0AT0d1AT1AT2AB2AB1AB0
Playthe programme on this machine; its Studio page's Machine pane is the machine as a programme
open the page, with its Report pane

the ring with docks: ring6d

S0S1S2S3S4S5A0A3
a six-site loop with dock spurs at S0 and S3; the spur ends A0 and A3 are trap sites (gates, measurement, cooling; capacity 2), the loop sites hold 2 and cannot gate

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=0

Expands 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")
well-typed and legal on ring6d · cost 10 · steps 7 · 430 µs · peak n̄ 15.22
S0S1d0S2S3S4d1S5A0A3
Playthe programme on this machine; its Studio page's Machine pane is the machine as a programme
open the page, with its Report pane

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

In this section what a generator is (a function from parameters to an explicit geometry), the seven shipped ones, and the rule that decides whether a file may store the parameters instead of the graph.

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.

Definition M16 generator. A generator is a function gen : Params → Geometry from a record of parameters to an explicit geometry (N, E, L). A document whose geometry is compact, generator(params), denotes gen(params). A geometry G is reproducible from (gen, params) when gen(params) = G exactly; the writer stores the compact form only then, and the explicit form otherwise, so that no file ever loads as a different machine from the one that was saved.

In words. A generator is a recipe; the graph is the dish. The file keeps the recipe only while it still makes this dish.

generatorparameterswhat it writes
ringwidth, height, verticals, dock_offset, cornersone 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
racetrackstraightone closed loop of 2·straight trap sites and no junction: every bend is ordinary rail
grida, ban a × b lattice of nodes with a trap in the middle of every wire: interior nodes degree 4, edge nodes 3, corners 2
chainnn sites in a line, one open loop, two ends of degree 1
ladderwidth, rungs, highwaystwo rails joined by rungs
dual_loopwidth, couplings, cornersan inner loop and an outer loop, joined by a rail at each coupling, where two sites become junctions
cylindera, bthe planar drawing of a periodic grid: the wrap in one direction, so that R21 holds
Example One generator, three parameter choices. The slot count, the dock positions and therefore the junctions all follow from the numbers; the document stores the numbers, and the loader writes the graph.
S0S1S2S3S4S5A0A3
ring(3, 2, 2): 8 sites, 2 junctions
S0S1S2S3S4S5S6S7A0A4
ring(4, 2, 2): 10 sites, 2 junctions
S0S1S2S3S4S5S6S7S8S9S10S11A0A3A6A9
ring(5, 3, 4): 16 sites, 4 junctions

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

In this section the listing as a language: its statements (twelve declarations, three edits, five builder verbs, four seeds) and the part of M each writes, the grammar's one deliberate absence (no expressions), a machine written by hand from three traps and a junction, and where to type one.

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.

Definition M17 listing. A listing of a machine M is a programme in the grammar below whose statements, run in order from an empty machine, construct M: ⟦listing(M)⟧ = M. The grammar has no expression production: a value is a literal, a list, a dictionary or 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
statementwriteswhich part of M
m = Machine.blank(gen, …, zones=[…])the geometry from a generator, and the zone names it may useG (compact), dom Z
d = DeviceBuilder(); d.site(id, x, y, zone=…, capacity=…)one siteN
d.junction(id, x, y)one bare junctionN
d.segment(id, a, b, loop=…, length=…, capacity=…)one railE
d.loop(id, [nodes], closed=…, kind=…)one loopL
m = Machine.blank_device(d.build(), …)seals an explicit geometry into a machineG (explicit)
m.set_zone(zone, capacity=…, gate=…, spam=…, cool=…)one zone typeZ
m.set_site_capacity(sites, n)a capacity a site keeps when its zone is retunedcap
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 classC
m.set_control(model=…, max_simd_classes_per_cycle=…, channels=…)the control model and its boundsC
m.set_wiring(scheme=…, dacs_dynamic=…, electrodes_per_trap=…)the wiring blockC
m.set_heating(…), m.set_species(…), m.set_budget(…), m.describe(…)the three records and the descriptionH, S, B, name
m.move_site(site, x, y), m.set_segment_length(…)edits to a geometry already sealedN, E
Example A machine written by hand: three traps A, B, C around one junction J, a T. Four builder statements place the nodes, three the rails, one seals the geometry; the zones, curves and control plane are then stated as on any other machine (here borrowed from the default template, so the machine can be played). The loader derives deg(J) = 3, so J is a junction and a hop from A to B crosses it at the degree-3 price.
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"])
Adeg 1Bdeg 1Cdeg 1Jdeg 3
tee: N = {A, B, C, J}, J a bare junction of degree 3; no loop
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"]])
well-typed and legal on tee · cost 7 · steps 7 · 615 µs · peak n̄ 9.23
Ad0BCd1J
Playthe programme on the hand-written machine; its page's Machine pane is the listing above
open the page, with its Report pane

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

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.