State injection
Bridge move.LogicalInitialize to Gemini state-injection kernel arguments.
On Gemini hardware a logical qubit is prepared by state injection: a single
physical qubit is rotated into the requested state and then encoded into the
Steane [[7,1,3]] code by a fixed pulse schedule. The pulse kernel that does
this (state_injection_init in qlue) is parameterised by, for each of
the two interleaved column groups of the Gemini grid, a y_mask selecting
the active rows plus per-row axis_angle / rotation_angle lists, and by
one shared CZ top-hat window.
Everything the bridge needs to know is a lanes convention:
- the angle units of
move.LogicalInitialize(turns,1.0 == 2*pi), - how a :class:
~bloqade.lanes.bytecode.encoding.LocationAddressmaps onto the(col_group, row)geometry of the Gemini arch spec, - the shape of the
y_maskand per-row angle lists.
Keeping the adapter here, next to the dialect it consumes, means it moves in lock-step with those conventions instead of drifting in a downstream repo.
Angle convention
move.LogicalInitialize carries the U3(theta, phi, lam) Euler angles that
prepare each logical qubit from |0>. Matching
U3(theta, phi, lam)|0> = cos(theta/2)|0> + e^{i phi} sin(theta/2)|1>
against a rotation R_n(beta) about an equatorial axis n at angle
alpha from X,
R_n(beta)|0> = cos(beta/2)|0> - i e^{i alpha} sin(beta/2)|1>,
gives beta = theta and alpha = phi + pi/2. In turns that is
rotation_angle = theta and axis_angle = phi + 0.25. lam only
contributes a global phase on |0> and is dropped. This is the inverse of
the axis_angle -> (phi, lam) mapping move2squin applies to
move.LocalR / move.GlobalR, so both directions agree.
The CZ top-hat window is a hardware calibration quantity expressed in the pulse kernels' own coordinate frame (which is not the arch spec's corner-at-zero frame), so it is always supplied by the caller.
N_COL_GROUPS
module-attribute
N_COL_GROUPS = 2
Number of interleaved column groups on the Gemini grid.
N_ROWS
module-attribute
N_ROWS = 5
Number of logical-qubit rows per column group on the Gemini grid.
QUARTER_TURN
module-attribute
QUARTER_TURN = 0.25
Offset (turns) from the phi Euler angle to the rotation-axis angle.
CZWindow
Bases: NamedTuple
flowchart TD
bloqade.lanes.arch.gemini.state_injection.CZWindow[CZWindow]
click bloqade.lanes.arch.gemini.state_injection.CZWindow href "" "bloqade.lanes.arch.gemini.state_injection.CZWindow"
CZ top-hat window in the pulse kernels' coordinate frame (micrometres).
ymin/ymax bound the rows that should see the CZ pulse;
ymin_keepout/ymax_keepout extend that by the keep-out margin. The
window is global in x and shared by both column groups, so it must
span every row that is active in either y_mask.
from_bounds
classmethod
from_bounds(
ymin: float, ymax: float, keep_out_buffer: float = 10.0
) -> CZWindow
Build a window from row bounds plus a symmetric keep-out margin.
Source code in .venv/lib/python3.14/site-packages/bloqade/lanes/arch/gemini/state_injection.py
83 84 85 86 87 88 | |
logical_initialize_to_state_injection_args
logical_initialize_to_state_injection_args(
node: LogicalInitialize,
arch_spec: ArchSpec,
cz_window: CZWindow,
*,
n_rows: int = N_ROWS,
atol: float = 1e-06
) -> tuple[ir.SSAValue, ...]
Build the state_injection_init argument list for a LogicalInitialize.
Inserts the supporting statements before node (which is left in
place for the caller to replace) and returns, in order::
(y_mask_col0, axis_angles_col0, rotation_angles_col0,
y_mask_col1, axis_angles_col1, rotation_angles_col1,
ymin, ymax, ymin_keepout, ymax_keepout)
Each y_mask is an IList[bool, n_rows] marking the rows of that
column group that hold a logical qubit; the aligned angle lists carry
that qubit's angles, or 0.0 for rows the mask switches off (inert, as
the mask removes them from the pulse entirely). Angles are in turns; see
the module docstring for the U3 -> (axis, rotation) derivation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node
|
LogicalInitialize
|
the |
required |
arch_spec
|
ArchSpec
|
the Gemini logical arch spec |
required |
cz_window
|
CZWindow
|
CZ top-hat window in the pulse kernels' frame. |
required |
n_rows
|
int
|
rows per column group expected by the pulse kernel. |
N_ROWS
|
atol
|
float
|
tolerance for matching site positions to the zone grid. |
1e-06
|
Raises:
| Type | Description |
|---|---|
ValueError
|
if the node is empty, spans several zones, has
mismatched argument lengths, holds a location
:func: |
Source code in .venv/lib/python3.14/site-packages/bloqade/lanes/arch/gemini/state_injection.py
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 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 | |
make_state_injection_args_getter
make_state_injection_args_getter(
arch_spec: ArchSpec,
cz_window: CZWindow,
*,
n_rows: int = N_ROWS,
atol: float = 1e-06
) -> Callable[
[move.LogicalInitialize], tuple[ir.SSAValue, ...]
]
Bind everything but the node, for RewriteLogicalInitialize-style hooks.
Downstream rewrites that swap a LogicalInitialize for a kernel
invocation take a node -> args callable; this returns one that calls
:func:logical_initialize_to_state_injection_args with the given spec
and window.
Source code in .venv/lib/python3.14/site-packages/bloqade/lanes/arch/gemini/state_injection.py
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 | |
resolve_col_group_and_row
resolve_col_group_and_row(
arch_spec: ArchSpec,
location: LocationAddress,
*,
atol: float = 1e-06
) -> tuple[int, int]
Resolve a Gemini logical-spec location to its (col_group, row).
The Gemini logical zone grid has four columns, left0, right0, left1,
right1: the left and right sites of a column-group-0 word pair, then
those of a column-group-1 pair. col_group is the pair the location's
column belongs to and row is the index of its y position.
The state-injection pulse only addresses the left site of each pair, so a right-site location is rejected rather than silently mapped onto its left partner. Physical-spec locations are rejected too: one logical qubit spans many physical sites, so a single physical site has no slot.
Raises:
| Type | Description |
|---|---|
ValueError
|
if |
Source code in .venv/lib/python3.14/site-packages/bloqade/lanes/arch/gemini/state_injection.py
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 138 139 140 141 142 143 144 145 146 | |
theta_phi_to_axis_rotation_angle
theta_phi_to_axis_rotation_angle(
theta: SSAValue,
phi: SSAValue,
insertion_point: Statement,
*,
quarter_turn: SSAValue | None = None
) -> tuple[ir.SSAValue, ir.SSAValue]
Insert IR mapping U3 (theta, phi) to (axis_angle, rotation_angle).
All values are in turns. rotation_angle is theta itself (no new
statement); axis_angle is phi + 0.25. Pass quarter_turn to
reuse an existing 0.25 constant instead of inserting a new one.
Source code in .venv/lib/python3.14/site-packages/bloqade/lanes/arch/gemini/state_injection.py
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | |