Skip to content

Index

AllToAllSiteTopology dataclass

AllToAllSiteTopology()

All-to-all site connectivity: one bus per (src, dst) pair.

For N sites, produces N*(N-1)/2 single-element buses allowing any site to reach any other site directly.

ArchBlueprint dataclass

ArchBlueprint(
    zones: dict[str, ZoneSpec],
    layout: DeviceLayout = DeviceLayout(),
    feed_forward: bool = False,
    atom_reloading: bool = False,
    blockade_radius: float | None = None,
)

High-level architecture definition composed of named zones and layout.

Zones are ordered by insertion order of the zones dict, which determines word ID assignment (contiguous per zone) and vertical layout (top to bottom).

Parameters:

Name Type Description Default
zones dict[str, ZoneSpec]

Named zones with their specifications.

required
layout DeviceLayout

Physical layout parameters for word/site placement.

DeviceLayout()
feed_forward bool

Whether the device supports feed-forward operations.

False
atom_reloading bool

Whether the device supports atom reloading.

False
blockade_radius float | None

Rydberg blockade radius (µm) to record on the ArchSpec. When set, this value is passed directly to ArchSpec.from_components and overrides any radius derived from ArchBuilder.set_blockade_radius or zone-level scans.

None

total_words property

total_words: int

Total number of words across all zones.

words_per_zone property

words_per_zone: int

Number of words per zone (all zones have equal grid dimensions).

zone_names property

zone_names: tuple[str, ...]

Zone names in definition order.

ArchBuilder

ArchBuilder()

Compose ZoneBuilders into a complete ArchSpec.

Each zone added gets assigned global word IDs. Inter-zone connections go into zone_buses. Calls Rust validation on build().

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/build/imperative.py
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
def __init__(self) -> None:
    self._zones: list[ZoneBuilder] = []
    self._zone_name_to_id: dict[str, int] = {}
    self._word_id_offsets: list[int] = []
    self._connections: list[
        tuple[tuple[str, Sequence[int]], tuple[str, Sequence[int]]]
    ] = []
    self._modes: list[tuple[str, list[str]]] = []
    self._total_words: int = 0
    self._blockade_radius: float | None = None

blockade_radius property

blockade_radius: float | None

Rydberg blockade radius (µm) applied to all zones, or None.

add_mode

add_mode(name: str, zones: Sequence[str]) -> None

Add an operational mode.

Parameters:

Name Type Description Default
name str

Mode name (e.g. "all", "gate", "measure").

required
zones Sequence[str]

Zone names to include in this mode.

required
Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/build/imperative.py
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
def add_mode(self, name: str, zones: Sequence[str]) -> None:
    """Add an operational mode.

    Args:
        name: Mode name (e.g. "all", "gate", "measure").
        zones: Zone names to include in this mode.
    """
    for z in zones:
        if z not in self._zone_name_to_id:
            raise ValueError(f"Unknown zone: '{z}'")
    self._modes.append((name, list(zones)))

add_zone

add_zone(zone: ZoneBuilder) -> int

Add a zone. Returns zone_id. Assigns global word IDs.

Validates that sites_per_word matches across all zones.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/build/imperative.py
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
def add_zone(self, zone: ZoneBuilder) -> int:
    """Add a zone. Returns zone_id. Assigns global word IDs.

    Validates that sites_per_word matches across all zones.
    """
    if zone.name in self._zone_name_to_id:
        raise ValueError(f"Duplicate zone name: '{zone.name}'")
    if self._zones:
        existing_spw = self._zones[0].sites_per_word
        if zone.sites_per_word != existing_spw:
            raise ValueError(
                f"sites_per_word mismatch: zone '{zone.name}' has "
                f"{zone.sites_per_word} but existing zones have "
                f"{existing_spw}"
            )
    zone_id = len(self._zones)
    self._zone_name_to_id[zone.name] = zone_id
    self._word_id_offsets.append(self._total_words)
    self._total_words += zone.num_words
    self._zones.append(zone)
    return zone_id

build

build(
    feed_forward: bool = False,
    atom_reloading: bool = False,
    blockade_radius: float | None = None,
) -> ArchSpec

Assemble the ArchSpec and validate via Rust.

Parameters:

Name Type Description Default
feed_forward bool

Whether the device supports feed-forward.

False
atom_reloading bool

Whether the device supports atom reloading.

False
blockade_radius float | None

Explicit blockade radius (µm). If provided, overrides both builder-level and zone-level radii.

None

Raises:

Type Description
ValueError

If Rust validation fails.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/build/imperative.py
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
def build(
    self,
    feed_forward: bool = False,
    atom_reloading: bool = False,
    blockade_radius: float | None = None,
) -> ArchSpec:
    """Assemble the ArchSpec and validate via Rust.

    Args:
        feed_forward: Whether the device supports feed-forward.
        atom_reloading: Whether the device supports atom reloading.
        blockade_radius: Explicit blockade radius (µm). If provided,
            overrides both builder-level and zone-level radii.

    Raises:
        ValueError: If Rust validation fails.
    """
    # 1. Collect all words with global IDs.
    all_words: list[Word] = []
    for zone in self._zones:
        for positions in zone._words:
            all_words.append(Word(tuple(positions)))

    # 2. Build Rust Zone objects.
    # Zone-local word IDs must be translated to global IDs for the Rust
    # ArchSpec, which uses global word indices everywhere.
    rust_zones: list[_RustZone] = []
    for zone_idx, zone in enumerate(self._zones):
        offset = self._word_id_offsets[zone_idx]
        site_buses = [_RustSiteBus(src=s, dst=d) for s, d in zone._site_buses]
        word_buses = [
            _RustWordBus(
                src=[offset + w for w in s],
                dst=[offset + w for w in d],
            )
            for s, d in zone._word_buses
        ]
        # Only words flagged at add_word(has_site_bus=True) are
        # eligible for site-bus transport. Default is True, so the
        # historical "all words opt-in when any site bus exists"
        # behavior is preserved unless the caller overrides.
        words_with_site_buses = (
            [
                offset + w
                for w in range(zone.num_words)
                if zone._word_has_site_bus[w]
            ]
            if site_buses
            else []
        )
        sites_with_word_buses = (
            list(range(zone.sites_per_word)) if word_buses else []
        )
        entangling_pairs = [
            (offset + a, offset + b) for a, b in zone._entangling_pairs
        ]
        rust_zones.append(
            _RustZone(
                name=zone.name,
                grid=zone._grid,
                site_buses=site_buses,
                word_buses=word_buses,
                words_with_site_buses=words_with_site_buses,
                sites_with_word_buses=sites_with_word_buses,
                entangling_pairs=entangling_pairs,
            )
        )

    # 3. Build zone_buses from connect() calls.
    zone_buses: list[_RustZoneBus] = []
    for (src_name, src_words), (dst_name, dst_words) in self._connections:
        src_zid = self._zone_name_to_id[src_name]
        dst_zid = self._zone_name_to_id[dst_name]
        src_offset = self._word_id_offsets[src_zid]
        dst_offset = self._word_id_offsets[dst_zid]
        zone_buses.append(
            _RustZoneBus(
                src=[(src_zid, src_offset + w) for w in src_words],
                dst=[(dst_zid, dst_offset + w) for w in dst_words],
            )
        )

    # 4. Build modes.
    modes: list[_RustMode] = []
    for mode_name, zone_names in self._modes:
        zone_ids = [self._zone_name_to_id[z] for z in zone_names]
        bitstring_order: list[_RustLocAddr] = []
        for zid in zone_ids:
            offset = self._word_id_offsets[zid]
            zone = self._zones[zid]
            for w in range(zone.num_words):
                for s in range(zone.sites_per_word):
                    bitstring_order.append(_RustLocAddr(zid, offset + w, s))
        modes.append(
            _RustMode(
                name=mode_name,
                zones=zone_ids,
                bitstring_order=bitstring_order,
            )
        )

    # 5. Compute AOD waypoint paths for site and word buses.
    all_paths: dict[LaneAddress, tuple[tuple[float, float], ...]] = {}
    for zone_idx, zone in enumerate(self._zones):
        offset = self._word_id_offsets[zone_idx]
        all_paths.update(zone._compute_paths(zone_idx, offset))

    # 6. Determine the blockade radius to record on the ArchSpec.
    # Precedence: explicit build(blockade_radius=...) argument >
    # builder-level set_blockade_radius > zone-level agreement > None.
    resolved_radius = (
        blockade_radius
        if blockade_radius is not None
        else self._resolve_blockade_radius()
    )

    # 7. Assemble and validate.
    return ArchSpec.from_components(
        words=tuple(all_words),
        zones=tuple(rust_zones),
        modes=modes,
        zone_buses=zone_buses,
        paths=all_paths or None,
        feed_forward=feed_forward,
        atom_reloading=atom_reloading,
        blockade_radius=resolved_radius,
    )

connect

connect(
    src: tuple[str, Sequence[int]],
    dst: tuple[str, Sequence[int]],
) -> None

Add an inter-zone bus (zone_buses).

Parameters:

Name Type Description Default
src tuple[str, Sequence[int]]

(zone_name, zone_local_word_indices) — typically from zone[...] (indexing the zone itself, which returns a name-qualified tuple).

required
dst tuple[str, Sequence[int]]

(zone_name, zone_local_word_indices) — same format.

required

Validates AOD Cartesian product across the two zone grids.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/build/imperative.py
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
def connect(
    self,
    src: tuple[str, Sequence[int]],
    dst: tuple[str, Sequence[int]],
) -> None:
    """Add an inter-zone bus (zone_buses).

    Args:
        src: ``(zone_name, zone_local_word_indices)`` — typically
            from ``zone[...]`` (indexing the zone itself, which
            returns a name-qualified tuple).
        dst: ``(zone_name, zone_local_word_indices)`` — same format.

    Validates AOD Cartesian product across the two zone grids.
    """
    src_name, src_words = src
    dst_name, dst_words = dst
    if src_name not in self._zone_name_to_id:
        raise ValueError(f"Unknown zone: '{src_name}'")
    if dst_name not in self._zone_name_to_id:
        raise ValueError(f"Unknown zone: '{dst_name}'")

    src_zone = self._zones[self._zone_name_to_id[src_name]]
    dst_zone = self._zones[self._zone_name_to_id[dst_name]]
    src_positions = [src_zone._word_origin(w) for w in src_words]
    dst_positions = [dst_zone._word_origin(w) for w in dst_words]
    _validate_aod_rectangle(src_positions, "Zone bus src")
    _validate_aod_rectangle(dst_positions, "Zone bus dst")

    self._connections.append((src, dst))

set_blockade_radius

set_blockade_radius(radius: float) -> None

Apply radius to every zone by calling :meth:ZoneBuilder.set_blockade_radius on each.

Overwrites every zone's entangling pairs with the scan result. The radius is stored on the builder and flows to ArchSpec.blockade_radius at :meth:build time.

The radius is validated up-front (positive, finite, nm-precise) before any zone is touched, and the scan is run two-phase: every zone is scanned before any pair list is overwritten, so a layout error in a later zone cannot leave earlier zones in a partially-updated state.

Parameters:

Name Type Description Default
radius float

Rydberg blockade radius in micrometers.

required

Raises:

Type Description
ValueError

if radius itself is invalid (non-positive, non-finite, sub-nm), or if any zone's layout is inconsistent with the radius. The error message includes the zone name and offending word IDs.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/build/imperative.py
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
def set_blockade_radius(self, radius: float) -> None:
    """Apply ``radius`` to every zone by calling
    :meth:`ZoneBuilder.set_blockade_radius` on each.

    Overwrites every zone's entangling pairs with the scan result.
    The radius is stored on the builder and flows to
    ``ArchSpec.blockade_radius`` at :meth:`build` time.

    The radius is validated up-front (positive, finite, nm-precise)
    *before* any zone is touched, and the scan is run two-phase:
    every zone is scanned before any pair list is overwritten, so
    a layout error in a later zone cannot leave earlier zones in a
    partially-updated state.

    Args:
        radius: Rydberg blockade radius in micrometers.

    Raises:
        ValueError: if ``radius`` itself is invalid (non-positive,
            non-finite, sub-nm), or if any zone's layout is
            inconsistent with the radius. The error message
            includes the zone name and offending word IDs.
    """
    if radius <= 0:
        raise ValueError(f"blockade_radius must be positive, got {radius}")
    radius_nm = _to_nm(radius, "blockade_radius")

    # Phase 1: scan every zone.  Any zone-level failure raises here
    # before we've mutated anything.
    scan_results: list[list[tuple[int, int]]] = [
        zone._scan_blockade_pairs(radius_nm) for zone in self._zones
    ]

    # Phase 2: commit.  No further failures possible.
    for zone, pairs in zip(self._zones, scan_results):
        zone._entangling_pairs = pairs
        zone._blockade_radius_nm = radius_nm
    self._blockade_radius = radius

ArchResult dataclass

ArchResult(
    arch: ArchSpec,
    zone_grids: dict[str, WordGrid],
    zone_indices: dict[str, int],
)

Result of build_arch(), containing the ArchSpec and metadata.

ArchSpec

ArchSpec(inner: ArchSpec)

Bases: RustWrapper[ArchSpec]


              flowchart TD
              bloqade.lanes.arch.ArchSpec[ArchSpec]
              bloqade.lanes.bytecode._wrapper.RustWrapper[RustWrapper]

                              bloqade.lanes.bytecode._wrapper.RustWrapper --> bloqade.lanes.arch.ArchSpec
                


              click bloqade.lanes.arch.ArchSpec href "" "bloqade.lanes.arch.ArchSpec"
              click bloqade.lanes.bytecode._wrapper.RustWrapper href "" "bloqade.lanes.bytecode._wrapper.RustWrapper"
            

Architecture specification for a quantum device.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/spec.py
36
37
38
def __init__(self, inner: _RustArchSpec):
    self._inner = inner
    self._inner.validate()

atom_reloading property

atom_reloading: bool

Whether the device supports reloading atoms after initial fill.

blockade_radius property

blockade_radius: float | None

Rydberg blockade radius (µm), or None if not provided.

This is metadata — when present, it indicates the radius associated with the architecture and is typically used to interpret the entangling pairs. It is not independently verified at the ArchSpec level; use :meth:ZoneBuilder.set_blockade_radius / :meth:ArchBuilder.set_blockade_radius if you want the pair list to be derived from and checked against a radius.

cz_zone_addresses cached property

cz_zone_addresses: frozenset[ZoneAddress]

Zones that host CZ entangling operations (have entangling_pairs).

feed_forward property

feed_forward: bool

Whether the device supports mid-circuit measurement with classical feedback.

home_sites cached property

home_sites: frozenset[LocationAddress]

All home LocationAddresses with correct zone_id per word.

A home site is (zone_id, word_id, site_id) where word_id is a home word (lower word_id in each entangling pair, or unpaired) and zone_id is the zone that word belongs to.

max_qubits property

max_qubits: int

Get the maximum number of qubits supported by this architecture.

paths cached property

paths: MappingProxyType[
    LaneAddress, tuple[tuple[float, float], ...]
]

Transport path waypoints keyed by LaneAddress.

Derived from the Rust ArchSpec.paths on first access.

site_buses cached property

site_buses: tuple[SiteBus, ...]

Aggregate all site buses across all zones.

Note: indices in this flat list do NOT correspond to per-zone bus_id values in LaneAddress. Prefer iterating zones directly via self.zones[i].site_buses.

sites_per_word property

sites_per_word: int

Get the number of sites per word.

word_buses cached property

word_buses: tuple[WordBus, ...]

Aggregate all word buses across all zones.

Note: indices in this flat list do NOT correspond to per-zone bus_id values in LaneAddress. Prefer iterating zones directly via self.zones[i].word_buses.

word_zone_map cached property

word_zone_map: dict[int, int]

Map each word_id to the zone_id it belongs to.

Delegates to Rust ArchSpec.word_zone_map().

words cached property

words: tuple[Word, ...]

Python Word wrappers, derived from the Rust ArchSpec.

check_lane_group

check_lane_group(
    lanes: Sequence[LaneAddress],
) -> Sequence[LaneGroupError]

Validate a group of lane addresses via Rust.

Checks individual lane validity, group consistency (direction, bus_id, move_type), bus membership, and AOD geometry constraints. Returns a list of LaneGroupError exceptions (empty if all valid).

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/spec.py
362
363
364
365
366
367
368
369
370
371
372
def check_lane_group(
    self, lanes: Sequence[LaneAddress]
) -> Sequence[LaneGroupError]:
    """Validate a group of lane addresses via Rust.

    Checks individual lane validity, group consistency (direction, bus_id,
    move_type), bus membership, and AOD geometry constraints.
    Returns a list of LaneGroupError exceptions (empty if all valid).
    """
    rust_addrs = [lane._inner for lane in lanes]
    return self._inner.check_lanes(rust_addrs)

check_location_group

check_location_group(
    locations: Sequence[LocationAddress],
) -> Sequence[LocationGroupError]

Validate a group of location addresses via Rust.

Returns a list of LocationGroupError exceptions (empty if all valid).

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/spec.py
352
353
354
355
356
357
358
359
360
def check_location_group(
    self, locations: Sequence[LocationAddress]
) -> Sequence[LocationGroupError]:
    """Validate a group of location addresses via Rust.

    Returns a list of LocationGroupError exceptions (empty if all valid).
    """
    rust_addrs = [loc._inner for loc in locations]
    return self._inner.check_locations(rust_addrs)

from_components classmethod

from_components(
    words: tuple[Word, ...],
    zones: tuple[Zone, ...],
    modes: Sequence[Mode],
    zone_buses: Sequence[ZoneBus] = (),
    paths: (
        dict[LaneAddress, tuple[tuple[float, float], ...]]
        | None
    ) = None,
    feed_forward: bool = False,
    atom_reloading: bool = False,
    blockade_radius: float | None = None,
) -> ArchSpec

Construct an ArchSpec from Python component types.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/spec.py
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
@classmethod
def from_components(
    cls,
    words: tuple[Word, ...],
    zones: tuple[_RustZone, ...],
    modes: Sequence[_RustMode],
    zone_buses: Sequence[ZoneBus] = (),
    paths: dict[LaneAddress, tuple[tuple[float, float], ...]] | None = None,
    feed_forward: bool = False,
    atom_reloading: bool = False,
    blockade_radius: float | None = None,
) -> ArchSpec:
    """Construct an ArchSpec from Python component types."""

    rust_paths = None
    if paths:
        rust_paths = [
            _RustTransportPath(
                lane=_RustLaneAddress(
                    lane.move_type,
                    lane.zone_id,
                    lane.word_id,
                    lane.site_id,
                    lane.bus_id,
                    lane.direction,
                ),
                waypoints=list(waypoints),
            )
            for lane, waypoints in paths.items()
        ]

    inner = _RustArchSpec(
        version=(2, 0),
        words=[w._inner for w in words],
        zones=list(zones),
        zone_buses=list(zone_buses),
        modes=list(modes),
        paths=rust_paths,
        feed_forward=feed_forward,
        atom_reloading=atom_reloading,
        blockade_radius=blockade_radius,
    )
    return cls(inner)

get_cz_partner

get_cz_partner(
    location: LocationAddress,
) -> LocationAddress | None

Get the CZ partner for a given location.

Uses Rust-side get_cz_partner which resolves via the zone's entangling_pairs.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/spec.py
414
415
416
417
418
419
420
421
422
423
def get_cz_partner(self, location: LocationAddress) -> LocationAddress | None:
    """Get the CZ partner for a given location.

    Uses Rust-side get_cz_partner which resolves via the zone's
    entangling_pairs.
    """
    result = self._inner.get_cz_partner(location._inner)
    if result is None:
        return None
    return LocationAddress.from_inner(result)

get_lane_address

get_lane_address(
    src: LocationAddress, dst: LocationAddress
) -> LaneAddress | None

Given an input tuple of locations, gets the lane (w/direction).

Delegates to Rust ArchSpec.lane_for_endpoints().

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/spec.py
374
375
376
377
378
379
380
381
382
383
384
def get_lane_address(
    self, src: LocationAddress, dst: LocationAddress
) -> LaneAddress | None:
    """Given an input tuple of locations, gets the lane (w/direction).

    Delegates to Rust ``ArchSpec.lane_for_endpoints()``.
    """
    result = self._inner.lane_for_endpoints(src._inner, dst._inner)
    if result is None:
        return None
    return LaneAddress.from_inner(result)

get_zone_index

get_zone_index(
    loc_addr: LocationAddress, zone_id: ZoneAddress
) -> int | None

O(1) flat index of a location within a zone.

Delegates to Rust ArchSpec.zone_location_index().

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/spec.py
318
319
320
321
322
323
324
325
326
327
def get_zone_index(
    self,
    loc_addr: LocationAddress,
    zone_id: ZoneAddress,
) -> int | None:
    """O(1) flat index of a location within a zone.

    Delegates to Rust ``ArchSpec.zone_location_index()``.
    """
    return self._inner.zone_location_index(loc_addr._inner, zone_id.zone_id)

is_home_position

is_home_position(addr: LocationAddress) -> bool

True if this address is at a home (non-CZ-staging) word.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/spec.py
125
126
127
def is_home_position(self, addr: LocationAddress) -> bool:
    """True if this address is at a home (non-CZ-staging) word."""
    return addr.word_id in self._home_words

iter_all_lanes

iter_all_lanes() -> Iterator[LaneAddress]

Yield every valid lane address in the architecture.

Enumerates site-bus, word-bus, and zone-bus lanes in both forward and backward directions. Used by :class:~bloqade.lanes.arch.metrics.MoveMetricCalculator to compute max-duration bounds. Prefer get_lane_address(src, dst) for single-pair lookups.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/spec.py
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
def iter_all_lanes(self) -> Iterator[LaneAddress]:
    """Yield every valid lane address in the architecture.

    Enumerates site-bus, word-bus, and zone-bus lanes in both forward
    and backward directions. Used by
    :class:`~bloqade.lanes.arch.metrics.MoveMetricCalculator` to compute
    max-duration bounds. Prefer ``get_lane_address(src, dst)`` for
    single-pair lookups.
    """
    sites_per_word = self.sites_per_word

    # Intra-zone: site buses and word buses.
    for zone_id, zone in enumerate(self._inner.zones):
        for bus_id, bus in enumerate(zone.site_buses):
            for word_id in zone.words_with_site_buses:
                for i in range(len(bus.src)):
                    for direction in (Direction.FORWARD, Direction.BACKWARD):
                        yield LaneAddress(
                            MoveType.SITE,
                            word_id,
                            bus.src[i],
                            bus_id,
                            direction,
                            zone_id,
                        )
        for bus_id, bus in enumerate(zone.word_buses):
            for site_id in zone.sites_with_word_buses:
                for word_id in bus.src:
                    for direction in (Direction.FORWARD, Direction.BACKWARD):
                        yield LaneAddress(
                            MoveType.WORD,
                            word_id,
                            site_id,
                            bus_id,
                            direction,
                            zone_id,
                        )

    # Inter-zone: zone buses.
    for bus_id, zb in enumerate(self._inner.zone_buses):
        for (src_zone, src_word), (_dst_zone, _dst_word) in zip(zb.src, zb.dst):
            for site_id in range(sites_per_word):
                for direction in (Direction.FORWARD, Direction.BACKWARD):
                    yield LaneAddress(
                        MoveType.ZONE,
                        src_word,
                        site_id,
                        bus_id,
                        direction,
                        src_zone,
                    )

to_json

to_json() -> str

Serialize this architecture spec to a JSON string.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/spec.py
262
263
264
def to_json(self) -> str:
    """Serialize this architecture spec to a JSON string."""
    return self._inner.to_json()

try_get_endpoints

try_get_endpoints(
    lane_address: LaneAddress,
) -> tuple[LocationAddress, LocationAddress] | None

Resolve lane_address to its (src, dst) location pair, or None when the lane is not a valid lane in the architecture (matches the Rust lane_endpoints contract).

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/spec.py
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
def try_get_endpoints(
    self, lane_address: LaneAddress
) -> tuple[LocationAddress, LocationAddress] | None:
    """Resolve ``lane_address`` to its (src, dst) location pair, or
    ``None`` when the lane is not a valid lane in the architecture
    (matches the Rust ``lane_endpoints`` contract).
    """
    result = self._inner.lane_endpoints(lane_address._inner)
    if result is None:
        return None
    rust_src, rust_dst = result
    return (
        LocationAddress.from_inner(rust_src),
        LocationAddress.from_inner(rust_dst),
    )

try_get_position

try_get_position(
    location: LocationAddress,
) -> tuple[float, float] | None

Resolve location to its physical (x, y) position, or None when location doesn't correspond to a valid site under the zone its zone_id selects (matches the Rust location_position contract).

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/spec.py
344
345
346
347
348
349
350
def try_get_position(self, location: LocationAddress) -> tuple[float, float] | None:
    """Resolve ``location`` to its physical (x, y) position, or
    ``None`` when ``location`` doesn't correspond to a valid site
    under the zone its ``zone_id`` selects (matches the Rust
    ``location_position`` contract).
    """
    return self._inner.location_position(location._inner)

yield_zone_locations

yield_zone_locations(
    zone_address: ZoneAddress,
) -> Iterator[LocationAddress]

Yield LocationAddresses in the canonical zone-bitstring iteration order.

This is the layout that get_zone_index(loc, zone_address) numbers: every (word, site) pair in the architecture, tagged with zone_address, walked in word-major then site-major order. The iterator visits every word — zone_address is the tag stamped onto each yielded address (and the grid through which downstream calls like get_position interpret it), not a membership filter.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/spec.py
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
def yield_zone_locations(
    self, zone_address: ZoneAddress
) -> Iterator[LocationAddress]:
    """Yield ``LocationAddress``es in the canonical zone-bitstring iteration order.

    This is the layout that ``get_zone_index(loc, zone_address)``
    numbers: every ``(word, site)`` pair in the architecture, tagged
    with ``zone_address``, walked in word-major then site-major order.
    The iterator visits every word — ``zone_address`` is the tag
    stamped onto each yielded address (and the grid through which
    downstream calls like ``get_position`` interpret it), not a
    membership filter.
    """
    zone_id = zone_address.zone_id
    for word_id in range(len(self.words)):
        word = self.words[word_id]
        for site_id in range(len(word.site_indices)):
            yield LocationAddress(word_id, site_id, zone_id)

ArchSpecGeometry

ArchSpecGeometry(arch_spec: 'ArchSpec | _RustArchSpec')

Geometry-level query helper wrapping an ArchSpec.

Provides methods for retrieving coordinate grids, flat site lists, and bus descriptors from an architecture specification. Intended for downstream consumers who need geometry-level data without walking Rust zone objects directly.

Accepts either a Python :class:~bloqade.lanes.arch.spec.ArchSpec or a raw Rust _RustArchSpec object, so it can be constructed from either layer of the stack.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/geometry.py
49
50
51
52
53
def __init__(self, arch_spec: "ArchSpec | _RustArchSpec") -> None:
    if isinstance(arch_spec, _RustArchSpec):
        self._inner = arch_spec
    else:
        self._inner = arch_spec._inner

get_all_sites

get_all_sites() -> list[tuple[float, float]]

Get all site positions across all zones in canonical order.

Returns positions in zone-major order, with each zone flattened in column-major grid order (x outer, y inner). Each position is an (x, y) tuple.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/geometry.py
79
80
81
82
83
84
85
86
87
88
89
90
91
def get_all_sites(self) -> list[tuple[float, float]]:
    """Get all site positions across all zones in canonical order.

    Returns positions in zone-major order, with each zone flattened
    in column-major grid order (``x`` outer, ``y`` inner).
    Each position is an ``(x, y)`` tuple.
    """
    sites: list[tuple[float, float]] = []
    for zone in self._inner.zones:
        for x in zone.grid.x_positions:
            for y in zone.grid.y_positions:
                sites.append((x, y))
    return sites

get_available_buses

get_available_buses(zone_id: int) -> list[BusDescriptor]

Enumerate all valid bus descriptors for a zone.

Parameters:

Name Type Description Default
zone_id int

Zone index.

required

Returns:

Type Description
list[BusDescriptor]

List of BusDescriptor for each (bus_id, move_type, direction)

list[BusDescriptor]

combination that has at least one lane in this zone.

Raises:

Type Description
ValueError

If zone_id is out of range.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/geometry.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
def get_available_buses(self, zone_id: int) -> list[BusDescriptor]:
    """Enumerate all valid bus descriptors for a zone.

    Args:
        zone_id: Zone index.

    Returns:
        List of ``BusDescriptor`` for each (bus_id, move_type, direction)
        combination that has at least one lane in this zone.

    Raises:
        ValueError: If zone_id is out of range.
    """
    if zone_id < 0 or zone_id >= len(self._inner.zones):
        raise ValueError(
            f"zone_id {zone_id} out of range [0, {len(self._inner.zones)})"
        )
    zone = self._inner.zones[zone_id]
    result: list[BusDescriptor] = []

    for bus_id, bus in enumerate(zone.site_buses):
        n = len(bus.src) * len(zone.words_with_site_buses)
        for direction in (Direction.FORWARD, Direction.BACKWARD):
            result.append(
                BusDescriptor(
                    bus_id=bus_id,
                    move_type=MoveType.SITE,
                    direction=direction,
                    num_lanes=n,
                )
            )

    for bus_id, bus in enumerate(zone.word_buses):
        n = len(bus.src) * len(zone.sites_with_word_buses)
        for direction in (Direction.FORWARD, Direction.BACKWARD):
            result.append(
                BusDescriptor(
                    bus_id=bus_id,
                    move_type=MoveType.WORD,
                    direction=direction,
                    num_lanes=n,
                )
            )

    return result

get_grid_endpoints

get_grid_endpoints(
    zone_id: int,
    bus_id: int,
    move_type: MoveType,
    direction: Direction,
) -> tuple[GeoGrid, GeoGrid]

Get start and end grids for a bus move at full occupancy.

Returns two bloqade.geometry.Grid objects representing the source and destination positions for all lanes in the specified bus group.

Parameters:

Name Type Description Default
zone_id int

Zone index.

required
bus_id int

Bus index within the zone.

required
move_type MoveType

SITE or WORD.

required
direction Direction

FORWARD or BACKWARD.

required

Returns:

Type Description
Grid

(src_grid, dst_grid) where each grid contains the physical

Grid

positions of all source/destination sites for this bus.

Raises:

Type Description
ValueError

If zone_id or bus_id is out of range, or move_type is not SITE or WORD.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/geometry.py
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
def get_grid_endpoints(
    self,
    zone_id: int,
    bus_id: int,
    move_type: MoveType,
    direction: Direction,
) -> tuple[GeoGrid, GeoGrid]:
    """Get start and end grids for a bus move at full occupancy.

    Returns two ``bloqade.geometry.Grid`` objects representing the
    source and destination positions for all lanes in the specified
    bus group.

    Args:
        zone_id: Zone index.
        bus_id: Bus index within the zone.
        move_type: SITE or WORD.
        direction: FORWARD or BACKWARD.

    Returns:
        ``(src_grid, dst_grid)`` where each grid contains the physical
        positions of all source/destination sites for this bus.

    Raises:
        ValueError: If zone_id or bus_id is out of range, or
            move_type is not SITE or WORD.
    """
    from bloqade.geometry.dialects.grid import Grid as GeoGrid

    if zone_id < 0 or zone_id >= len(self._inner.zones):
        raise ValueError(
            f"zone_id {zone_id} out of range [0, {len(self._inner.zones)})"
        )
    zone = self._inner.zones[zone_id]

    src_positions: list[tuple[float, float]] = []
    dst_positions: list[tuple[float, float]] = []

    if move_type == MoveType.SITE:
        if bus_id < 0 or bus_id >= len(zone.site_buses):
            raise ValueError(
                f"site bus_id {bus_id} out of range [0, {len(zone.site_buses)})"
            )
        bus = zone.site_buses[bus_id]
        for word_id in zone.words_with_site_buses:
            for src_site, _dst_site in zip(bus.src, bus.dst, strict=True):
                lane = LaneAddress(
                    move_type, word_id, src_site, bus_id, direction, zone_id
                )
                endpoints = self._inner.lane_endpoints(lane._inner)
                if endpoints is None:
                    raise ValueError(
                        f"lane {lane!r} has no endpoints in zone {zone_id}"
                    )
                src_loc, dst_loc = endpoints
                src_pos = self._inner.location_position(src_loc)
                if src_pos is None:
                    raise ValueError(
                        f"location {src_loc!r} has no position in zone {zone_id}"
                    )
                dst_pos = self._inner.location_position(dst_loc)
                if dst_pos is None:
                    raise ValueError(
                        f"location {dst_loc!r} has no position in zone {zone_id}"
                    )
                src_positions.append(src_pos)
                dst_positions.append(dst_pos)
    elif move_type == MoveType.WORD:
        if bus_id < 0 or bus_id >= len(zone.word_buses):
            raise ValueError(
                f"word bus_id {bus_id} out of range [0, {len(zone.word_buses)})"
            )
        bus = zone.word_buses[bus_id]
        for src_word, _dst_word in zip(bus.src, bus.dst, strict=True):
            for site_id in zone.sites_with_word_buses:
                lane = LaneAddress(
                    move_type, src_word, site_id, bus_id, direction, zone_id
                )
                endpoints = self._inner.lane_endpoints(lane._inner)
                if endpoints is None:
                    raise ValueError(
                        f"lane {lane!r} has no endpoints in zone {zone_id}"
                    )
                src_loc, dst_loc = endpoints
                src_pos = self._inner.location_position(src_loc)
                if src_pos is None:
                    raise ValueError(
                        f"location {src_loc!r} has no position in zone {zone_id}"
                    )
                dst_pos = self._inner.location_position(dst_loc)
                if dst_pos is None:
                    raise ValueError(
                        f"location {dst_loc!r} has no position in zone {zone_id}"
                    )
                src_positions.append(src_pos)
                dst_positions.append(dst_pos)
    else:
        raise ValueError(f"Unsupported move_type: {move_type}")

    src_xs = sorted(set(p[0] for p in src_positions))
    src_ys = sorted(set(p[1] for p in src_positions))
    dst_xs = sorted(set(p[0] for p in dst_positions))
    dst_ys = sorted(set(p[1] for p in dst_positions))

    return (
        GeoGrid.from_positions(tuple(src_xs), tuple(src_ys)),
        GeoGrid.from_positions(tuple(dst_xs), tuple(dst_ys)),
    )

get_zone_grid

get_zone_grid(zone_id: int) -> GeoGrid

Get the coordinate grid for a zone as a bloqade.geometry.Grid.

Parameters:

Name Type Description Default
zone_id int

Zone index.

required

Returns:

Type Description
Grid

A bloqade.geometry.dialects.grid.Grid with the zone's

Grid

x and y positions.

Raises:

Type Description
ValueError

If zone_id is out of range.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/geometry.py
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
def get_zone_grid(self, zone_id: int) -> GeoGrid:
    """Get the coordinate grid for a zone as a ``bloqade.geometry.Grid``.

    Args:
        zone_id: Zone index.

    Returns:
        A ``bloqade.geometry.dialects.grid.Grid`` with the zone's
        x and y positions.

    Raises:
        ValueError: If zone_id is out of range.
    """
    from bloqade.geometry.dialects.grid import Grid as GeoGrid

    if zone_id < 0 or zone_id >= len(self._inner.zones):
        raise ValueError(
            f"zone_id {zone_id} out of range [0, {len(self._inner.zones)})"
        )
    zone = self._inner.zones[zone_id]
    return GeoGrid.from_positions(
        tuple(zone.grid.x_positions), tuple(zone.grid.y_positions)
    )

BusDescriptor dataclass

BusDescriptor(
    bus_id: int,
    move_type: MoveType,
    direction: Direction,
    num_lanes: int,
)

Descriptor for a bus within a zone.

DeviceLayout dataclass

DeviceLayout(
    sites_per_word: int = 5,
    site_spacing: float = 10.0,
    pair_spacing: float = 10.0,
    row_spacing: float = 20.0,
    zone_gap: float = 20.0,
    x_clearance: float = 3.0,
    y_clearance: float = 3.0,
)

Physical layout parameters for word and site placement.

Words are horizontal rows with interleaved CZ pairs. Within a pair, sites alternate: even word at x=0, 2s, 4s, ... and odd word at x=s, 3s, 5s, ... where s = site_spacing (blockade radius).

Parameters:

Name Type Description Default
sites_per_word int

Number of sites per word.

5
site_spacing float

Distance between adjacent atoms (micrometers). Also determines the CZ blockade distance between paired sites.

10.0
pair_spacing float

Horizontal gap between adjacent CZ pairs (micrometers).

10.0
row_spacing float

Vertical distance between word grid rows (micrometers).

20.0
zone_gap float

Additional vertical gap between zones (micrometers).

20.0
x_clearance float

Minimum x-axis clearance (µm) between AOD path waypoints and grid lines.

3.0
y_clearance float

Minimum y-axis clearance (µm) between AOD path waypoints and grid lines. Separate x/y values are useful when row and column spacings differ substantially.

3.0

DiagonalWordTopology dataclass

DiagonalWordTopology()

Diagonal word connectivity between adjacent column pairs.

For a grid of (N rows x C cols), applies diagonal connectivity between each adjacent column pair (col_i, col_{i+1}). Per pair, produces 2N - 1 buses. Total buses: (C-1) * (2N - 1).

Per column pair: - Group 1 (shift 0..N-1): col_a[r] -> col_b[r + shift] for r in 0..N-1-shift - Group 2 (shift 1..N-1): col_a[r + shift] -> col_b[r] for r in 0..N-1-shift (reverse diagonal)

This gives full connectivity between all word pairs in adjacent columns, organized by diagonal. Non-adjacent columns are reachable via multi-hop.

HypercubeSiteTopology dataclass

HypercubeSiteTopology()

Hypercube site connectivity within a single word.

For N = 2^k sites, produces k buses. Bus for dimension d connects sites that differ in bit d: src = [sites with bit d=0], dst = [sites with bit d=1]. Each bus has N/2 parallel moves.

For non-power-of-2 N, rounds up to the next power of 2 and filters out site indices >= N. Higher-indexed sites get fewer connections (e.g. for N=17, site 16 connects only to site 0 via dimension 4).

HypercubeWordTopology dataclass

HypercubeWordTopology()

Hypercube word connectivity along both row and column dimensions.

For a grid of (R x C) words where R = 2^r and C = 2^c, produces r + c buses. Row dimension d connects word(row, col) to word(row ^ 2^d, col). Column dimension d connects word(row, col) to word(row, col ^ 2^d).

InterZoneTopology

Bases: Protocol


              flowchart TD
              bloqade.lanes.arch.InterZoneTopology[InterZoneTopology]

              

              click bloqade.lanes.arch.InterZoneTopology href "" "bloqade.lanes.arch.InterZoneTopology"
            

Generates word buses connecting words across two zones.

MatchingTopology dataclass

MatchingTopology()

1:1 matching of words by grid position across two zones.

Produces a single bus pairing grid_a(r, c) with grid_b(r, c) for all (r, c). Requires both grids to have the same dimensions.

MoveMetricCalculator dataclass

MoveMetricCalculator(arch_spec: Any)

Move-metric computation: lane durations, distances, and costs.

Owns timing constants extracted from bloqade-flair and provides cached lane duration / cost lookups. Lives in the layout package so that PathFinder and heuristics can consume it without pulling in the heavy compilation imports of Metrics.

get_lane_duration_cost

get_lane_duration_cost(
    lane_address: Any, *, amplitude_delta: float = 1.0
) -> float

Return normalized lane duration cost in [0, 1].

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/metrics.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def get_lane_duration_cost(
    self, lane_address: Any, *, amplitude_delta: float = 1.0
) -> float:
    """Return normalized lane duration cost in [0, 1]."""
    max_duration_us = self._max_lane_duration_us(amplitude_delta=amplitude_delta)
    if max_duration_us <= 0.0:
        return 0.0
    lane_duration_us = self.get_lane_duration_us(
        lane_address, amplitude_delta=amplitude_delta
    )
    return min(1.0, max(0.0, lane_duration_us / max_duration_us))

get_lane_duration_us

get_lane_duration_us(
    lane_address: Any, *, amplitude_delta: float = 1.0
) -> float

Return lane execution duration in microseconds.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/metrics.py
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
def get_lane_duration_us(
    self, lane_address: Any, *, amplitude_delta: float = 1.0
) -> float:
    """Return lane execution duration in microseconds."""
    normalized_amp = abs(float(amplitude_delta))
    cache_key = (lane_address, normalized_amp)
    if (duration_us := self._lane_duration_cache_us.get(cache_key)) is not None:
        return duration_us

    segment_distances = self.path_segment_distances_um(
        self.arch_spec.get_path(lane_address)
    )
    ramp_time_us = normalized_amp / self._FLAIR_MAX_RAMP_US
    duration_us = (
        ramp_time_us
        + sum(self._const_jerk_min_duration_us(dist) for dist in segment_distances)
        + ramp_time_us
    )
    self._lane_duration_cache_us[cache_key] = duration_us
    return duration_us

lane_distance_um

lane_distance_um(lane: Any) -> float

Total distance in µm along a lane's path.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/metrics.py
106
107
108
109
def lane_distance_um(self, lane: Any) -> float:
    """Total distance in µm along a lane's path."""
    path = self.arch_spec.get_path(lane)
    return sum(self.path_segment_distances_um(path))

PathFinder dataclass

PathFinder(spec: ArchSpec)

physical_address_map class-attribute instance-attribute

physical_address_map: dict[LocationAddress, int] = field(
    init=False, default_factory=dict
)

Map from (zone_id, word_id, site_id) tuple to graph node index.

physical_addresses class-attribute instance-attribute

physical_addresses: list[LocationAddress] = field(
    init=False, default_factory=list
)

Map from graph node index to (zone_id, word_id, site_id) tuple.

site_graph class-attribute instance-attribute

site_graph: PyDiGraph = field(
    init=False, default_factory=PyDiGraph
)

Graph representing all sites and edges as lanes.

extract_lanes_from_path

extract_lanes_from_path(
    path: list[int],
) -> tuple[LaneAddress, ...]

Given a path as node indices, extract the lane addresses.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/path.py
132
133
134
135
136
137
138
139
140
141
142
143
144
def extract_lanes_from_path(self, path: list[int]) -> tuple[LaneAddress, ...]:
    """Given a path as node indices, extract the lane addresses."""
    if len(path) < 2:
        raise ValueError("Path must have at least two nodes to extract lanes.")
    lanes = []
    for start_node, end_node in zip(path, path[1:]):
        lane = self.site_graph.get_edge_data(start_node, end_node)
        if lane is None:
            raise ValueError(
                f"No lane exists between nodes {start_node} and {end_node}."
            )
        lanes.append(lane)
    return tuple(lanes)

extract_locations_from_path

extract_locations_from_path(
    path: list[int],
) -> tuple[LocationAddress, ...]

Given a path as node indices, extract the location addresses.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/path.py
146
147
148
149
150
def extract_locations_from_path(
    self, path: list[int]
) -> tuple[LocationAddress, ...]:
    """Given a path as node indices, extract the location addresses."""
    return tuple(self.physical_addresses[ele] for ele in path)

find_path

find_path(
    start: LocationAddress,
    end: LocationAddress,
    occupied: frozenset[LocationAddress] = frozenset(),
    path_heuristic: Callable[
        [
            tuple[LaneAddress, ...],
            tuple[LocationAddress, ...],
        ],
        float,
    ] = lambda _, __: 0.0,
    edge_weight: (
        Callable[[LaneAddress], float] | None
    ) = None,
) -> (
    tuple[
        tuple[LaneAddress, ...], tuple[LocationAddress, ...]
    ]
    | None
)

Find a weighted shortest path from start to end.

Parameters:

Name Type Description Default
start LocationAddress

The starting location.

required
end LocationAddress

The ending location.

required
occupied frozenset[LocationAddress]

Locations to exclude when searching for a path. If this excludes start or end, no path is returned.

frozenset()
path_heuristic Callable[[tuple[LaneAddress, ...], tuple[LocationAddress, ...]], float]

A tie-breaker over candidate shortest paths, evaluated on the candidate location sequence.

lambda _, __: 0.0
edge_weight Callable[[LaneAddress], float] | None

Optional edge weight function used for shortest-path costs. Defaults to Metrics.get_lane_duration_us when not provided.

None

Returns:

Type Description
tuple[tuple[LaneAddress, ...], tuple[LocationAddress, ...]] | None

A tuple containing: - The selected path as LaneAddress values. - The same path as LocationAddress values (including start and end).

tuple[tuple[LaneAddress, ...], tuple[LocationAddress, ...]] | None

Returns None when no valid path exists.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/path.py
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
def find_path(
    self,
    start: LocationAddress,
    end: LocationAddress,
    occupied: frozenset[LocationAddress] = frozenset(),
    path_heuristic: Callable[
        [tuple[LaneAddress, ...], tuple[LocationAddress, ...]], float
    ] = lambda _, __: 0.0,
    edge_weight: Callable[[LaneAddress], float] | None = None,
) -> tuple[tuple[LaneAddress, ...], tuple[LocationAddress, ...]] | None:
    """Find a weighted shortest path from start to end.

    Args:
        start: The starting location.
        end: The ending location.
        occupied: Locations to exclude when searching for a path. If this excludes
            `start` or `end`, no path is returned.
        path_heuristic: A tie-breaker over candidate shortest paths, evaluated on
            the candidate location sequence.
        edge_weight: Optional edge weight function used for shortest-path costs.
            Defaults to `Metrics.get_lane_duration_us` when not provided.

    Returns:
        A tuple containing:
            - The selected path as `LaneAddress` values.
            - The same path as `LocationAddress` values (including start and end).
        Returns `None` when no valid path exists.
    """
    start_node = self.physical_address_map.get(start)
    end_node = self.physical_address_map.get(end)
    if start_node is None or end_node is None:
        return None
    if start == end:
        return (), (start,)

    available_nodes = [
        node
        for node, address in enumerate(self.physical_addresses)
        if address not in occupied
    ]
    subgraph, node_map = self.site_graph.subgraph_with_nodemap(available_nodes)
    original_to_subgraph = {original: sub for sub, original in node_map.items()}

    if (
        start_node not in original_to_subgraph
        or end_node not in original_to_subgraph
    ):
        return None

    if edge_weight is None:
        resolved_edge_weight = self.metrics.get_lane_duration_us
    else:
        resolved_edge_weight = edge_weight

    try:
        path_nodes = nx.all_shortest_paths(
            subgraph,
            original_to_subgraph[start_node],
            original_to_subgraph[end_node],
            weight_fn=resolved_edge_weight,
        )
    except nx.NoPathFound:
        return None
    original_paths = ([node_map[node] for node in path] for path in path_nodes)
    paths = [
        (
            self.extract_lanes_from_path(original_path),
            self.extract_locations_from_path(original_path),
        )
        for original_path in original_paths
        if len(original_path) >= 2
    ]
    if len(paths) == 0:
        return None
    return min(paths, key=lambda p: path_heuristic(*p))

get_endpoints

get_endpoints(lane: LaneAddress)

Get the start and end LocationAddress for a given LaneAddress.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/path.py
165
166
167
168
169
def get_endpoints(self, lane: LaneAddress):
    """Get the start and end LocationAddress for a given LaneAddress."""
    if lane in self.end_points_cache:
        return self.end_points_cache[lane]
    return None, None

get_lane

get_lane(
    start: LocationAddress, end: LocationAddress
) -> LaneAddress | None

Get the LaneAddress connecting two LocationAddress sites.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/path.py
152
153
154
155
156
157
158
159
160
161
162
163
def get_lane(
    self, start: LocationAddress, end: LocationAddress
) -> LaneAddress | None:
    """Get the LaneAddress connecting two LocationAddress sites."""
    start_node = self.physical_address_map.get(start)
    end_node = self.physical_address_map.get(end)
    if start_node is None or end_node is None:
        return None
    edge_data = self.site_graph.get_edge_data(start_node, end_node)
    if edge_data is None:
        return None
    return edge_data

SiteTopology

Bases: Protocol


              flowchart TD
              bloqade.lanes.arch.SiteTopology[SiteTopology]

              

              click bloqade.lanes.arch.SiteTopology href "" "bloqade.lanes.arch.SiteTopology"
            

Generates site buses for movement within a single word (row of sites).

TransversalSiteTopology dataclass

TransversalSiteTopology(
    logical_topology: SiteTopology,
    code_distance: int,
    intra_group_topology: SiteTopology | None = None,
)

Physical site topology derived from a logical site topology via code expansion.

Sites are organized in groups of code_distance. Group g contains physical sites [g*d, g*d+1, ..., g*d+d-1] where d = code_distance.

Produces two kinds of buses (transversal buses first so that logical bus IDs are preserved):

  1. Transversal buses -- each logical bus is "inflated" so that every (src, dst) element becomes d parallel physical elements. Logical bus B becomes physical bus B with the same index.

  2. Intra-group buses (optional) -- generated by intra_group_topology for d sites, then replicated and offset for each group. These support non-transversal operations within a code word (e.g. Steane code initialisation, syndrome extraction).

WordGrid dataclass

WordGrid(
    words: tuple[Word, ...],
    num_rows: int,
    num_cols: int,
    word_id_offset: int,
)

2D grid of words within a zone, preserving row/col structure.

all_word_ids property

all_word_ids: range

All word IDs in this grid (contiguous range).

cz_pairs

cz_pairs() -> Iterator[tuple[int, int]]

Yield (word_id_a, word_id_b) for all CZ entangling pairs.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/build/word_factory.py
47
48
49
50
51
def cz_pairs(self) -> Iterator[tuple[int, int]]:
    """Yield (word_id_a, word_id_b) for all CZ entangling pairs."""
    for row in range(self.num_rows):
        for col in range(0, self.num_cols, 2):
            yield (self.word_id_at(row, col), self.word_id_at(row, col + 1))

word_at

word_at(row: int, col: int) -> Word

Get the word at a given grid position.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/build/word_factory.py
34
35
36
def word_at(self, row: int, col: int) -> Word:
    """Get the word at a given grid position."""
    return self.words[row * self.num_cols + col]

word_id_at

word_id_at(row: int, col: int) -> int

Get the global word ID at a given grid position.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/build/word_factory.py
38
39
40
def word_id_at(self, row: int, col: int) -> int:
    """Get the global word ID at a given grid position."""
    return self.word_id_offset + row * self.num_cols + col

WordTopology

Bases: Protocol


              flowchart TD
              bloqade.lanes.arch.WordTopology[WordTopology]

              

              click bloqade.lanes.arch.WordTopology href "" "bloqade.lanes.arch.WordTopology"
            

Generates word buses for movement between words in a 2D grid.

ZoneBuilder

ZoneBuilder(
    name: str,
    grid: Grid,
    word_shape: tuple[int, int],
    *,
    x_clearance: float,
    y_clearance: float
)

Build a single zone with its words, grid, and buses.

All indices are zone-local. Words are placed on the zone's grid and validated for shape and overlap. Buses are validated for AOD Cartesian product compliance.

Parameters:

Name Type Description Default
name str

Human-readable zone name (stored in Rust Zone).

required
grid Grid

Coordinate grid for this zone. Every x- and y-position must be representable at 1 nm precision (i.e., at most 3 decimal places when given in µm).

required
word_shape tuple[int, int]

(num_x_sites, num_y_sites) — uniform shape for all words in this zone. sites_per_word = product of shape.

required
x_clearance float

Minimum physical distance (> 0, µm) from grid lines that path waypoints must maintain on the x-axis. Must be representable at 1 nm precision.

required
y_clearance float

Same as x_clearance, applied to the y-axis. Allowing separate values is useful when row and column spacings differ substantially (e.g., tight intra-pair x spacing but wide row spacing).

required

Raises:

Type Description
ValueError

If either clearance is not positive or any position / clearance value is not nm-precise.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/build/imperative.py
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
def __init__(
    self,
    name: str,
    grid: _RustGrid,
    word_shape: tuple[int, int],
    *,
    x_clearance: float,
    y_clearance: float,
):
    """Initialize a zone.

    Args:
        name: Human-readable zone name (stored in Rust Zone).
        grid: Coordinate grid for this zone.  Every x- and y-position
            must be representable at 1 nm precision (i.e., at most 3
            decimal places when given in µm).
        word_shape: (num_x_sites, num_y_sites) — uniform shape for
            all words in this zone. sites_per_word = product of shape.
        x_clearance: Minimum physical distance (> 0, µm) from grid
            lines that path waypoints must maintain on the x-axis.
            Must be representable at 1 nm precision.
        y_clearance: Same as ``x_clearance``, applied to the y-axis.
            Allowing separate values is useful when row and column
            spacings differ substantially (e.g., tight intra-pair x
            spacing but wide row spacing).

    Raises:
        ValueError: If either clearance is not positive or any
            position / clearance value is not nm-precise.
    """
    if x_clearance <= 0:
        raise ValueError(f"x_clearance must be positive, got {x_clearance}")
    if y_clearance <= 0:
        raise ValueError(f"y_clearance must be positive, got {y_clearance}")
    self._name = name
    self._grid = grid
    self._word_shape = word_shape
    # Internal nm-integer representation for exact path search.
    self._x_clearance_nm = _to_nm(x_clearance, "x_clearance")
    self._y_clearance_nm = _to_nm(y_clearance, "y_clearance")
    self._grid_x_nm: list[int] = [
        _to_nm(x, "grid x-position") for x in grid.x_positions
    ]
    self._grid_y_nm: list[int] = [
        _to_nm(y, "grid y-position") for y in grid.y_positions
    ]
    self._words: list[list[tuple[int, int]]] = []
    self._word_has_site_bus: list[bool] = []
    self._position_to_word: dict[tuple[int, int], int] = {}
    self._site_buses: list[tuple[list[int], list[int]]] = []
    self._word_buses: list[tuple[list[int], list[int]]] = []
    self._entangling_pairs: list[tuple[int, int]] = []
    self._blockade_radius_nm: int | None = None

blockade_radius property

blockade_radius: float | None

Rydberg blockade radius (µm) used to derive entangling pairs, or None.

name property

name: str

Zone name.

num_words property

num_words: int

Number of words added so far.

sites property

sites: _SiteGridQuery

Query site indices within the word shape.

sites_per_word property

sites_per_word: int

Total sites per word (product of word_shape).

word_shape property

word_shape: tuple[int, int]

(num_x_sites, num_y_sites) for each word.

words property

words: _WordGridQuery

Query word indices by grid region for intra-zone use.

Returns a plain list[int] of zone-local word indices whose sites intersect the queried region — suitable for passing directly to add_word_bus / add_entangling_pairs.

For cross-zone references (e.g. ArchBuilder.connect), index the zone itself (zone[region]) to get a name-qualified (name, list[int]) tuple.

x_clearance property

x_clearance: float

Minimum x-axis clearance (µm) from grid lines for waypoints.

y_clearance property

y_clearance: float

Minimum y-axis clearance (µm) from grid lines for waypoints.

__getitem__

__getitem__(
    key: tuple[
        slice | int | Sequence[int],
        slice | int | Sequence[int],
    ],
) -> tuple[str, list[int]]

Query word indices by grid region, name-qualified for cross-zone use.

Returns (self.name, zone.words[key]) so the result can be passed directly to ArchBuilder.connect(src=..., dst=...), which expects (zone_name, zone_local_indices).

For intra-zone use (passing indices to add_word_bus etc.), use zone.words[key] which returns just the index list.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/build/imperative.py
554
555
556
557
558
559
560
561
562
563
564
565
566
def __getitem__(
    self, key: tuple[slice | int | Sequence[int], slice | int | Sequence[int]]
) -> tuple[str, list[int]]:
    """Query word indices by grid region, name-qualified for cross-zone use.

    Returns ``(self.name, zone.words[key])`` so the result can be
    passed directly to ``ArchBuilder.connect(src=..., dst=...)``,
    which expects ``(zone_name, zone_local_indices)``.

    For intra-zone use (passing indices to ``add_word_bus`` etc.),
    use ``zone.words[key]`` which returns just the index list.
    """
    return (self._name, self.words[key])

add_entangling_pairs

add_entangling_pairs(
    words_a: Sequence[int], words_b: Sequence[int]
) -> None

Mark paired zone-local words as CZ pairs.

words_a[i] is paired with words_b[i]. The two sequences must have the same length.

For most users, prefer :meth:set_blockade_radius — it derives the pair list directly from geometry and validates the word layout against the CZ-pairing convention.

Any blockade_radius previously recorded on this zone (via :meth:set_blockade_radius) is cleared, since a manual append means the pair list is no longer purely radius-derived.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/build/imperative.py
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
def add_entangling_pairs(
    self, words_a: Sequence[int], words_b: Sequence[int]
) -> None:
    """Mark paired zone-local words as CZ pairs.

    ``words_a[i]`` is paired with ``words_b[i]``. The two sequences
    must have the same length.

    For most users, prefer :meth:`set_blockade_radius` — it derives
    the pair list directly from geometry and validates the word
    layout against the CZ-pairing convention.

    Any ``blockade_radius`` previously recorded on this zone (via
    :meth:`set_blockade_radius`) is cleared, since a manual append
    means the pair list is no longer purely radius-derived.
    """
    if len(words_a) != len(words_b):
        raise ValueError(
            f"words_a has {len(words_a)} entries but words_b has " f"{len(words_b)}"
        )
    n = len(self._words)
    for a, b in zip(words_a, words_b):
        if a < 0 or a >= n:
            raise ValueError(f"word index {a} out of range [0, {n})")
        if b < 0 or b >= n:
            raise ValueError(f"word index {b} out of range [0, {n})")
        self._entangling_pairs.append((a, b))
    # Pair list was just manually modified; the cached radius no
    # longer describes its full state.
    self._blockade_radius_nm = None

add_site_bus

add_site_bus(
    src: Sequence[int], dst: Sequence[int]
) -> None

Add a site bus (intra-word movement).

src/dst are site indices within word_shape (0..sites_per_word). Must have equal length. Validates that src and dst positions each form a valid AOD Cartesian product on the word grid.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/build/imperative.py
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
def add_site_bus(self, src: Sequence[int], dst: Sequence[int]) -> None:
    """Add a site bus (intra-word movement).

    src/dst are site indices within word_shape (0..sites_per_word).
    Must have equal length. Validates that src and dst positions each
    form a valid AOD Cartesian product on the word grid.
    """
    if len(src) != len(dst):
        raise ValueError(
            f"Site bus src has {len(src)} entries but dst has {len(dst)}"
        )
    total = self.sites_per_word
    nx = self._word_shape[0]
    for s in src:
        if s < 0 or s >= total:
            raise ValueError(f"site index {s} out of range [0, {total})")
    for d in dst:
        if d < 0 or d >= total:
            raise ValueError(f"site index {d} out of range [0, {total})")

    src_positions = [(s % nx, s // nx) for s in src]
    dst_positions = [(d % nx, d // nx) for d in dst]
    _validate_aod_rectangle(src_positions, "Site bus src")
    _validate_aod_rectangle(dst_positions, "Site bus dst")
    self._site_buses.append((list(src), list(dst)))

add_word

add_word(
    x_sites: slice | Sequence[int],
    y_sites: slice | Sequence[int],
    *,
    has_site_bus: bool = True
) -> int

Add a word occupying the given grid positions.

The number of x-indices and y-indices must match word_shape. Grid positions must not overlap with any existing word.

Parameters:

Name Type Description Default
x_sites slice | Sequence[int]

Grid x-indices for the word's sites.

required
y_sites slice | Sequence[int]

Grid y-indices for the word's sites.

required
has_site_bus bool

Whether this word participates in site-bus transport. Feeds the zone-level words_with_site_buses list on the final ArchSpec — only words with has_site_bus=True are eligible to have site buses applied to them. Defaults to True, which preserves the historical "all words opt-in" behavior. Set to False on storage words that shouldn't participate in site-level routing.

True

Returns:

Type Description
int

Zone-local word index.

Raises:

Type Description
ValueError

Shape mismatch or grid position overlap.

IndexError

Indices out of range for this zone's grid.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/build/imperative.py
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
def add_word(
    self,
    x_sites: slice | Sequence[int],
    y_sites: slice | Sequence[int],
    *,
    has_site_bus: bool = True,
) -> int:
    """Add a word occupying the given grid positions.

    The number of x-indices and y-indices must match word_shape.
    Grid positions must not overlap with any existing word.

    Args:
        x_sites: Grid x-indices for the word's sites.
        y_sites: Grid y-indices for the word's sites.
        has_site_bus: Whether this word participates in site-bus
            transport. Feeds the zone-level
            ``words_with_site_buses`` list on the final ``ArchSpec``
            — only words with ``has_site_bus=True`` are eligible to
            have site buses applied to them. Defaults to ``True``,
            which preserves the historical "all words opt-in"
            behavior. Set to ``False`` on storage words that
            shouldn't participate in site-level routing.

    Returns:
        Zone-local word index.

    Raises:
        ValueError: Shape mismatch or grid position overlap.
        IndexError: Indices out of range for this zone's grid.
    """
    xs = _normalize_index(x_sites, self._grid.num_x)
    ys = _normalize_index(y_sites, self._grid.num_y)

    if len(xs) != self._word_shape[0]:
        raise ValueError(
            f"x_sites has {len(xs)} indices but word_shape requires "
            f"{self._word_shape[0]}"
        )
    if len(ys) != self._word_shape[1]:
        raise ValueError(
            f"y_sites has {len(ys)} indices but word_shape requires "
            f"{self._word_shape[1]}"
        )

    for x in xs:
        if x < 0 or x >= self._grid.num_x:
            raise IndexError(
                f"x index {x} out of range for grid with "
                f"{self._grid.num_x} x-positions"
            )
    for y in ys:
        if y < 0 or y >= self._grid.num_y:
            raise IndexError(
                f"y index {y} out of range for grid with "
                f"{self._grid.num_y} y-positions"
            )

    positions = [(x, y) for y in ys for x in xs]
    for pos in positions:
        if pos in self._position_to_word:
            owner = self._position_to_word[pos]
            raise ValueError(
                f"Grid position (x={pos[0]}, y={pos[1]}) "
                f"already belongs to word {owner}"
            )

    word_id = len(self._words)
    self._words.append(positions)
    self._word_has_site_bus.append(has_site_bus)
    for pos in positions:
        self._position_to_word[pos] = word_id
    return word_id

add_word_bus

add_word_bus(
    src: Sequence[int], dst: Sequence[int]
) -> None

Add a word bus (intra-zone movement).

src/dst are zone-local word indices. Must have equal length. Validates that src and dst word positions each form a valid AOD Cartesian product on the zone grid.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/build/imperative.py
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def add_word_bus(self, src: Sequence[int], dst: Sequence[int]) -> None:
    """Add a word bus (intra-zone movement).

    src/dst are zone-local word indices. Must have equal length.
    Validates that src and dst word positions each form a valid AOD
    Cartesian product on the zone grid.
    """
    if len(src) != len(dst):
        raise ValueError(
            f"Word bus src has {len(src)} entries but dst has {len(dst)}"
        )
    n = len(self._words)
    for s in src:
        if s < 0 or s >= n:
            raise ValueError(f"word index {s} out of range [0, {n})")
    for d in dst:
        if d < 0 or d >= n:
            raise ValueError(f"word index {d} out of range [0, {n})")

    src_positions = [self._word_origin(s) for s in src]
    dst_positions = [self._word_origin(d) for d in dst]
    _validate_aod_rectangle(src_positions, "Word bus src")
    _validate_aod_rectangle(dst_positions, "Word bus dst")
    self._word_buses.append((list(src), list(dst)))

set_blockade_radius

set_blockade_radius(radius: float) -> None

Derive entangling word pairs from the Rydberg blockade radius.

Scans every pair of distinct words in the zone and classifies each under the matching-site-index CZ convention:

  • All matching-index site distances <= radius and all non-matching-index site distances > radius: valid CZ pair.
  • Some matching-index distances within radius, some outside: ValueError (partial blockade — the word layout doesn't cleanly map onto the CZ-pairing convention).
  • Any non-matching-index site distance within radius (regardless of whether the matching-index distances also fall within): ValueError (crossed-index — two words are arranged such that site i of one word sits next to site j != i of the other, violating the exclusivity the convention requires).
  • All distances outside radius: words ignore each other, no pair recorded.

After classification, every word must appear in at most one valid pair; multiple partners raise ValueError.

This call overwrites _entangling_pairs with the scan result and stores the radius on the zone. To have it flow into the final ArchSpec.blockade_radius, either call :meth:ArchBuilder.set_blockade_radius (which applies to every zone and records the value at builder scope) or, for a single zone already set via ZoneBuilder.set_blockade_radius, ArchBuilder.build() will pick up a consistent zone-level radius automatically.

Parameters:

Name Type Description Default
radius float

Blockade radius in micrometers. Must be positive and representable at 1 nm precision.

required

Raises:

Type Description
ValueError

if the layout is inconsistent with the radius (partial blockade / crossed-index / multi-partner) or if radius is not positive / nm-precise.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/build/imperative.py
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
def set_blockade_radius(self, radius: float) -> None:
    """Derive entangling word pairs from the Rydberg blockade radius.

    Scans every pair of distinct words in the zone and classifies
    each under the matching-site-index CZ convention:

    * All matching-index site distances ``<= radius`` and all
      non-matching-index site distances ``> radius``: valid CZ pair.
    * Some matching-index distances within radius, some outside:
      ``ValueError`` (partial blockade — the word layout doesn't
      cleanly map onto the CZ-pairing convention).
    * Any non-matching-index site distance within radius (regardless
      of whether the matching-index distances also fall within):
      ``ValueError`` (crossed-index — two words are arranged such
      that site ``i`` of one word sits next to site ``j != i`` of
      the other, violating the exclusivity the convention requires).
    * All distances outside radius: words ignore each other, no
      pair recorded.

    After classification, every word must appear in **at most one**
    valid pair; multiple partners raise ``ValueError``.

    This call **overwrites** ``_entangling_pairs`` with the scan
    result and stores the radius on the zone.  To have it flow into
    the final ``ArchSpec.blockade_radius``, either call
    :meth:`ArchBuilder.set_blockade_radius` (which applies to every
    zone and records the value at builder scope) or, for a single
    zone already set via ``ZoneBuilder.set_blockade_radius``,
    ``ArchBuilder.build()`` will pick up a consistent zone-level
    radius automatically.

    Args:
        radius: Blockade radius in micrometers. Must be positive and
            representable at 1 nm precision.

    Raises:
        ValueError: if the layout is inconsistent with the radius
            (partial blockade / crossed-index / multi-partner) or
            if ``radius`` is not positive / nm-precise.
    """
    if radius <= 0:
        raise ValueError(f"blockade_radius must be positive, got {radius}")
    radius_nm = _to_nm(radius, "blockade_radius")
    self._entangling_pairs = self._scan_blockade_pairs(radius_nm)
    self._blockade_radius_nm = radius_nm

ZoneSpec dataclass

ZoneSpec(
    num_rows: int,
    num_cols: int,
    entangling: bool = False,
    measurement: bool = True,
    word_topology: WordTopology | None = None,
    site_topology: SiteTopology | None = None,
)

Specification for a single zone in a multi-zone architecture.

Words are arranged in a 2D grid (num_rows x num_cols). Horizontally adjacent word pairs form CZ entangling pairs in entangling zones.

Parameters:

Name Type Description Default
num_rows int

Number of rows in the word grid. Must be >= 1.

required
num_cols int

Number of columns in the word grid. Must be >= 2 and even.

required
entangling bool

Whether this zone supports CZ entangling gates.

False
measurement bool

Whether this zone supports measurement.

True

num_words property

num_words: int

Total number of words in this zone.

build_arch

build_arch(
    blueprint: ArchBlueprint,
    connections: (
        dict[tuple[str, str], InterZoneTopology] | None
    ) = None,
) -> ArchResult

Build an ArchSpec from a blueprint and inter-zone connections.

One blueprint zone maps to one Rust zone. Entangling pairs are metadata on the zone, not a reason to split into sub-zones.

Parameters:

Name Type Description Default
blueprint ArchBlueprint

Architecture blueprint with zones and layout.

required
connections dict[tuple[str, str], InterZoneTopology] | None

Inter-zone connectivity. Keys are (zone_a, zone_b) name pairs, values are InterZoneTopology instances.

None

Returns:

Type Description
ArchResult

ArchResult with the validated ArchSpec and metadata.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/build/blueprint.py
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
def build_arch(
    blueprint: ArchBlueprint,
    connections: dict[tuple[str, str], InterZoneTopology] | None = None,
) -> ArchResult:
    """Build an ArchSpec from a blueprint and inter-zone connections.

    One blueprint zone maps to one Rust zone. Entangling pairs are
    metadata on the zone, not a reason to split into sub-zones.

    Args:
        blueprint: Architecture blueprint with zones and layout.
        connections: Inter-zone connectivity. Keys are (zone_a, zone_b) name
            pairs, values are InterZoneTopology instances.

    Returns:
        ArchResult with the validated ArchSpec and metadata.
    """
    connections = connections or {}
    layout = blueprint.layout
    n = layout.sites_per_word
    s = layout.site_spacing

    # Validate connections reference valid zones.
    for zone_a, zone_b in connections:
        if zone_a == zone_b:
            raise ValueError(
                f"Self-connection not allowed: '{zone_a}'. "
                "Use word_topology on ZoneSpec for intra-zone connectivity."
            )
        if zone_a not in blueprint.zones:
            raise ValueError(f"Unknown zone '{zone_a}' in connection")
        if zone_b not in blueprint.zones:
            raise ValueError(f"Unknown zone '{zone_b}' in connection")

    # 1. Create word grids (preserves row/col structure for topology generators).
    zone_grids: dict[str, WordGrid] = {}
    word_id_offset = 0
    for zone_name, zone_spec in blueprint.zones.items():
        grid = create_zone_words(
            zone_spec,
            layout,
            word_id_offset=word_id_offset,
        )
        zone_grids[zone_name] = grid
        word_id_offset += zone_spec.num_words

    # 2. Build ZoneBuilders from blueprint zones.
    zone_builders: dict[str, ZoneBuilder] = {}
    y_offset = 0.0

    for zone_name, zone_spec in blueprint.zones.items():
        word_grid = zone_grids[zone_name]
        rust_grid = _build_zone_grid(zone_spec, layout, n, s, y_offset=y_offset)
        # Advance y_offset past this zone's rows + the inter-zone gap.
        zone_height = max(0, zone_spec.num_rows - 1) * layout.row_spacing
        y_offset += zone_height + layout.zone_gap
        word_shape = _word_shape_from_layout(zone_spec, layout)

        zone = ZoneBuilder(
            zone_name,
            rust_grid,
            word_shape,
            x_clearance=layout.x_clearance,
            y_clearance=layout.y_clearance,
        )

        # Place words on the grid using the same index pattern as create_zone_words.
        for row in range(zone_spec.num_rows):
            for col in range(zone_spec.num_cols):
                word = word_grid.word_at(row, col)
                # Extract x and y indices from the word's site positions.
                x_indices = sorted({site[0] for site in word.site_indices})
                y_indices = sorted({site[1] for site in word.site_indices})
                zone.add_word(x_indices, y_indices)

        # Site buses from topology.
        if zone_spec.site_topology is not None:
            for bus in zone_spec.site_topology.generate_site_buses(
                layout.sites_per_word
            ):
                zone.add_site_bus(list(bus.src), list(bus.dst))

        # Intra-zone word buses from topology.
        if zone_spec.word_topology is not None:
            for bus in zone_spec.word_topology.generate_word_buses(word_grid):
                # Topology generators use global word IDs; convert to zone-local.
                offset = word_grid.word_id_offset
                zone.add_word_bus(
                    src=[w - offset for w in bus.src],
                    dst=[w - offset for w in bus.dst],
                )

        # Entangling pairs.
        if zone_spec.entangling:
            offset = word_grid.word_id_offset
            pairs = list(word_grid.cz_pairs())
            zone.add_entangling_pairs(
                [a - offset for a, _ in pairs],
                [b - offset for _, b in pairs],
            )

        zone_builders[zone_name] = zone

    # 3. Compose zones into ArchBuilder.
    arch_builder = ArchBuilder()
    zone_indices: dict[str, int] = {}

    for zone_name, zone in zone_builders.items():
        zid = arch_builder.add_zone(zone)
        zone_indices[zone_name] = zid

    # 4. Inter-zone connections → zone_buses.
    for (zone_a_name, zone_b_name), topology in connections.items():
        grid_a = zone_grids[zone_a_name]
        grid_b = zone_grids[zone_b_name]
        offset_a = grid_a.word_id_offset
        offset_b = grid_b.word_id_offset

        for bus in topology.generate_word_buses(grid_a, grid_b):
            # Convert global word IDs to zone-local for connect().
            src_local = [w - offset_a for w in bus.src]
            dst_local = [w - offset_b for w in bus.dst]
            arch_builder.connect(
                src=(zone_a_name, src_local),
                dst=(zone_b_name, dst_local),
            )

    # 5. Modes.
    all_zone_names = list(blueprint.zones.keys())
    arch_builder.add_mode("all", all_zone_names)

    for name, spec in blueprint.zones.items():
        if spec.measurement:
            arch_builder.add_mode(name, [name])

    # 6. Build and return.
    arch = arch_builder.build(
        feed_forward=blueprint.feed_forward,
        atom_reloading=blueprint.atom_reloading,
        blockade_radius=blueprint.blockade_radius,
    )

    return ArchResult(
        arch=arch,
        zone_grids=zone_grids,
        zone_indices=zone_indices,
    )

create_zone_words

create_zone_words(
    zone_spec: ZoneSpec,
    layout: DeviceLayout,
    x_offset: float = 0.0,
    y_offset: float = 0.0,
    word_id_offset: int = 0,
) -> WordGrid

Create all words for a zone in a 2D grid layout.

Each word's sites are represented as grid index pairs (x_idx, y_idx). For interleaved CZ pairs: - Even-column words: sites at x_idx = 0, 2, 4, ... (in the full grid) - Odd-column words: sites at x_idx = 1, 3, 5, ...

Words in different rows use different y_idx values.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/arch/build/word_factory.py
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
def create_zone_words(
    zone_spec: ZoneSpec,
    layout: DeviceLayout,
    x_offset: float = 0.0,
    y_offset: float = 0.0,
    word_id_offset: int = 0,
) -> WordGrid:
    """Create all words for a zone in a 2D grid layout.

    Each word's sites are represented as grid index pairs (x_idx, y_idx).
    For interleaved CZ pairs:
    - Even-column words: sites at x_idx = 0, 2, 4, ... (in the full grid)
    - Odd-column words: sites at x_idx = 1, 3, 5, ...

    Words in different rows use different y_idx values.
    """
    n = layout.sites_per_word
    num_cols = zone_spec.num_cols

    words: list[Word] = []

    for row in range(zone_spec.num_rows):
        for col in range(num_cols):
            pair_idx = col // 2
            is_odd = col % 2
            # Number of x positions per pair: 2*n (interleaved)
            x_base = pair_idx * 2 * n
            if is_odd:
                # Odd column: sites at odd x indices
                sites = tuple((x_base + 2 * i + 1, row) for i in range(n))
            else:
                # Even column: sites at even x indices
                sites = tuple((x_base + 2 * i, row) for i in range(n))
            words.append(Word(sites))

    return WordGrid(
        words=tuple(words),
        num_rows=zone_spec.num_rows,
        num_cols=zone_spec.num_cols,
        word_id_offset=word_id_offset,
    )