Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion source/pip/qsharp/qre/_architecture.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def add_instruction(
error_rate: float | _FloatFunction = 0.0,
transform: ISATransform | None = None,
source: list[Instruction] | None = None,
**kwargs: int,
**kwargs: int | float,
) -> int:
"""
Create an instruction and add it to the provenance graph.
Expand Down
23 changes: 17 additions & 6 deletions source/pip/qsharp/qre/_instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def constraint(
*,
arity: Optional[int] = 1,
error_rate: Optional[ConstraintBound] = None,
**kwargs: bool,
**kwargs: bool | ConstraintBound,
) -> Constraint:
"""
Create an instruction constraint.
Expand All @@ -55,8 +55,10 @@ def constraint(
arity (Optional[int]): The instruction arity. If None, instruction is
assumed to have variable arity. Default is 1.
error_rate (Optional[ConstraintBound]): The constraint on the error rate.
**kwargs (bool): Required properties that matching instructions must have.
Valid property names: distance. Set to True to require the property.
**kwargs (bool | ConstraintBound): Required property conditions for
matching instructions. Pass ``True`` to require that the property
exists, or pass a ``ConstraintBound`` to require a numeric
property value to satisfy that bound.
Returns:
Constraint: The instruction constraint.
Expand All @@ -67,11 +69,20 @@ def constraint(
c = Constraint(id, encoding, arity, error_rate)

for key, value in kwargs.items():
if value:
if (prop_key := property_name_to_key(key)) is None:
raise ValueError(f"Unknown property '{key}'")
if not value:
continue

if (prop_key := property_name_to_key(key)) is None:
raise ValueError(f"Unknown property '{key}'")

if value is True:
c.add_property(prop_key)
elif isinstance(value, ConstraintBound):
c.add_property_bound(prop_key, value)
else:
raise TypeError(
f"Property constraint '{key}' must be a bool or ConstraintBound"
)

return c

Expand Down
31 changes: 22 additions & 9 deletions source/pip/qsharp/qre/_qre.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -377,25 +377,25 @@ class Instruction:
"""
...

def set_property(self, key: int, value: int) -> None:
def set_property(self, key: int, value: bool | int | float | str) -> None:
"""
Set a property on the instruction.

Args:
key (int): The property key.
value (int): The property value.
value (bool | int | float | str): The property value.
"""
...

def get_property(self, key: int) -> Optional[int]:
def get_property(self, key: int) -> Optional[bool | int | float | str]:
"""
Get a property by its key.

Args:
key (int): The property key.

Returns:
Optional[int]: The property value, or None if not found.
Optional[bool | int | float | str]: The property value, or None if not found.
"""
...

Expand All @@ -411,28 +411,30 @@ class Instruction:
"""
...

def get_property_or(self, key: int, default: int) -> int:
def get_property_or(
self, key: int, default: bool | int | float | str
) -> bool | int | float | str:
"""
Get a property by its key, or return a default value if not found.

Args:
key (int): The property key.
default (int): The default value to return if the property is not found.
default (bool | int | float | str): The default value to return if the property is not found.

Returns:
int: The property value, or the default value if not found.
bool | int | float | str: The property value, or the default value if not found.
"""
...

def __getitem__(self, key: int) -> int:
def __getitem__(self, key: int) -> bool | int | float | str:
"""
Get a property by its key, or raise an error if not found.

Args:
key (int): The property key.

Returns:
int: The property value.
bool | int | float | str: The property value.
"""
...

Expand Down Expand Up @@ -593,6 +595,17 @@ class Constraint:
"""
...

def add_property_bound(self, property: int, bound: ConstraintBound) -> None:
"""
Add a numeric property bound requirement to the constraint.

Args:
property (int): The property key to constrain.
bound (ConstraintBound): The numeric bound that matching instructions
must satisfy for this property.
"""
...

def has_property(self, property: int) -> bool:
"""
Check if the constraint requires a specific property.
Expand Down
5 changes: 4 additions & 1 deletion source/pip/qsharp/qre/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,22 @@
from .factories import Litinski19Factory, MagicUpToClifford, RoundBasedFactory
from .qec import (
SurfaceCode,
SurfaceCodeLowMove,
ThreeAux,
OneDimensionalYokedSurfaceCode,
TwoDimensionalYokedSurfaceCode,
)
from .qubits import GateBased, Majorana
from .qubits import GateBased, Majorana, NeutralAtom

__all__ = [
"GateBased",
"Litinski19Factory",
"Majorana",
"MagicUpToClifford",
"NeutralAtom",
"RoundBasedFactory",
"SurfaceCode",
"SurfaceCodeLowMove",
"ThreeAux",
"OneDimensionalYokedSurfaceCode",
"TwoDimensionalYokedSurfaceCode",
Expand Down
2 changes: 2 additions & 0 deletions source/pip/qsharp/qre/models/qec/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
# Licensed under the MIT License.

from ._surface_code import SurfaceCode
from ._surface_code_low_move import SurfaceCodeLowMove
from ._three_aux import ThreeAux
from ._yoked import OneDimensionalYokedSurfaceCode, TwoDimensionalYokedSurfaceCode

__all__ = [
"SurfaceCode",
"SurfaceCodeLowMove",
"ThreeAux",
"OneDimensionalYokedSurfaceCode",
"TwoDimensionalYokedSurfaceCode",
Expand Down
216 changes: 216 additions & 0 deletions source/pip/qsharp/qre/models/qec/_surface_code_low_move.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

from __future__ import annotations
from dataclasses import KW_ONLY, dataclass, field
import math
from typing import Generator, Optional
from ..._instruction import (
ISA,
ISARequirements,
ISATransform,
constraint,
ConstraintBound,
LOGICAL,
)
from ..._isa_enumeration import ISAContext
from ..._qre import linear_function
from ...instruction_ids import (
CZ,
LATTICE_SURGERY,
MEAS_RESET_Z,
MEAS_Z,
PHYSICAL_MOVE,
RZ,
SQRT_X,
)
from ...property_keys import (
ATOM_SPACING,
VELOCITY,
ACCELERATION,
)


@dataclass
class SurfaceCodeLowMove(ISATransform):
"""
This class models a rotated surface code tailored to a reconfigurable,
zoned neutral-atom architecture with mobile ancillas.

The syndrome-extraction schedule is based on a mobile-ancilla surface-code
scheme in which a single ancilla visits the data qubits of each plaquette,
combined with the atom-transport model used by ``NeutralAtom``. In this
model, the ancilla is moved within the Rydberg interaction range of each
data atom to execute the entangling sequence, while other atoms and gate
sites remain separated by about 10 microns to suppress crosstalk. The time
model therefore combines the single-ancilla plaquette circuit with explicit
motion overhead from horizontal and diagonal transport segments.

Attributes:
crossing_prefactor: float
The prefactor for logical error rate due to error correction
crossings. (Default is 0.03, see Eq. (11) in
[arXiv:1208.0928](https://arxiv.org/abs/1208.0928))
error_correction_threshold: float
The error correction threshold for the surface code. (Default is
0.01 (1%), see [arXiv:1009.3686](https://arxiv.org/abs/1009.3686))
code_cycle_override: Optional[int]
If provided, this value will be used as the time for each syndrome
extraction cycle instead of the default calculation based on gate
times and transport overhead. (Default is None)
code_cycle_offset: int
An additional time offset to add to the syndrome extraction cycle
time. (Default is 0)

Hyper parameters:
distance: int
The code distance of the surface code.

References:

- D. S. Wang, A. G. Fowler, L. C. L. Hollenberg: Quantum computing with
nearest neighbor interactions and error rates over 1%,
[arXiv:1009.3686](https://arxiv.org/abs/1009.3686)
- D. Horsman, A. G. Fowler, S. Devitt, R. Van Meter: Surface code quantum
computing by lattice surgery,
[arXiv:1111.4022](https://arxiv.org/abs/1111.4022)
- A. G. Fowler, M. Mariantoni, J. M. Martinis, A. N. Cleland: Surface
codes: Towards practical large-scale quantum computation,
[arXiv:1208.0928](https://arxiv.org/abs/1208.0928)
- D. Bluvstein, H. Levine, G. Semeghini, et al.: A quantum processor based
on coherent transport of entangled atom arrays,
[arXiv:2112.03923](https://arxiv.org/abs/2112.03923)
- D. Bluvstein, S. J. Evered, A. A. Geim, et al.: Logical quantum
processor based on reconfigurable atom arrays,
[arXiv:2312.03982](https://arxiv.org/abs/2312.03982)
- S. Jandura, L. Pecorari, G. Pupillo: Surface Code Stabilizer
Measurements for Rydberg Atoms,
[arXiv:2405.16621](https://arxiv.org/abs/2405.16621)
- W.-H. Lin, D. B. Tan, J. Cong: Reuse-Aware Compilation for Zoned Quantum
Architectures Based on Neutral Atoms,
[arXiv:2411.11784](https://arxiv.org/abs/2411.11784)
- D. Bluvstein, A. A. Geim, S. H. Li, et al.: Architectural mechanisms of
a universal fault-tolerant quantum computer,
[arXiv:2506.20661](https://arxiv.org/abs/2506.20661)
"""

crossing_prefactor: float = 0.03
error_correction_threshold: float = 0.01
code_cycle_override: Optional[int] = None
code_cycle_offset: int = 0
_: KW_ONLY
distance: int = field(default=3, metadata={"domain": range(3, 26, 2)})

@staticmethod
def required_isa() -> ISARequirements:
return ISARequirements(
constraint(RZ, error_rate=ConstraintBound.lt(0.01)),
constraint(SQRT_X, error_rate=ConstraintBound.lt(0.01)),
constraint(CZ, arity=2, error_rate=ConstraintBound.lt(0.01)),
constraint(MEAS_Z, error_rate=ConstraintBound.lt(0.01)),
constraint(MEAS_RESET_Z, error_rate=ConstraintBound.lt(0.01)),
constraint(
PHYSICAL_MOVE,
error_rate=ConstraintBound.lt(0.01),
atom_spacing=ConstraintBound.gt(9.9),
),
)

def provided_isa(
self, impl_isa: ISA, ctx: ISAContext
) -> Generator[ISA, None, None]:
cz = impl_isa[CZ]
rz = impl_isa[RZ]
sqrt_x = impl_isa[SQRT_X]
reset = impl_isa[MEAS_RESET_Z]
meas_z = impl_isa[MEAS_Z]

move = impl_isa[PHYSICAL_MOVE]
atom_spacing_prop = move.get_property_or(ATOM_SPACING, 10.0)
max_vel_prop = move.get_property_or(VELOCITY, 0.25)
max_accel_prop = move.get_property_or(ACCELERATION, 5000.0)
assert isinstance(atom_spacing_prop, (int, float))
assert isinstance(max_vel_prop, (int, float))
assert isinstance(max_accel_prop, (int, float))
atom_spacing = float(atom_spacing_prop) * 1e-6 # Convert from microns to meters
max_vel = float(max_vel_prop)
max_accel = float(max_accel_prop)
if atom_spacing < max_vel**2 / max_accel:
hor_seg_time = math.sqrt(atom_spacing / max_accel)
else:
extra_distance = atom_spacing - max_vel**2 / max_accel
hor_seg_time = max_vel / max_accel + extra_distance / max_vel
if math.sqrt(2) * atom_spacing < max_vel**2 / max_accel:
diag_seg_time = math.sqrt(math.sqrt(2) * atom_spacing / max_accel)
else:
extra_distance = math.sqrt(2) * atom_spacing - max_vel**2 / max_accel
diag_seg_time = max_vel / max_accel + extra_distance / max_vel
move_time = 3 * move.expect_time() + 1e9 * (2 * hor_seg_time + diag_seg_time)

four_cz_time = math.ceil(4 * cz.expect_time() + move_time)
h_time = sqrt_x.expect_time() + 2 * rz.expect_time()
meas_time = meas_z.expect_time()
reset_time = reset.expect_time()

physical_error_rate = max(
rz.expect_error_rate(),
cz.expect_error_rate(),
sqrt_x.expect_error_rate(),
reset.expect_error_rate(),
meas_z.expect_error_rate(),
)

# There are d^2 data qubits and (d^2 - 1) ancilla qubits in the rotated
# surface code. (See Section 7.1 in arXiv:1111.4022)
# Unchanged from the original SurfaceCode.
space_formula = linear_function(2 * self.distance**2 - 1)

# Each standard syndrome extraction cycle consists of ancilla preparation, 4
# rounds of CNOTs, and measurement. (See Fig. 2 in arXiv:1009.3686).
Comment thread
brad-lackey marked this conversation as resolved.
# But this must be modified to acount for the fact that the CNOTs are
# implemented as CZ+sqrt(X). The syndrome extraction cycle
# is repeated d times for a distance-d code.
if self.code_cycle_override is not None:
code_cycle_time = self.code_cycle_override + self.code_cycle_offset
else:
if reset_time > four_cz_time:
code_cycle_time = (
max(reset_time, h_time)
+ (self.distance + 1)
* (reset_time + h_time + self.code_cycle_offset)
+ meas_time
)
else:
code_cycle_time = (
max(reset_time, h_time)
+ (self.distance + 1)
* (four_cz_time + h_time + self.code_cycle_offset)
+ meas_time
)
time_value = code_cycle_time * self.distance

# See Eqs. (10) and (11) in arXiv:1208.0928
error_formula = linear_function(
self.crossing_prefactor
* (
(physical_error_rate / self.error_correction_threshold)
** ((self.distance + 1) // 2)
)
)

# We provide a generic lattice surgery instruction (See Section 3 in
# arXiv:1111.4022)
yield ctx.make_isa(
ctx.add_instruction(
LATTICE_SURGERY,
encoding=LOGICAL,
arity=None,
space=space_formula,
time=time_value,
error_rate=error_formula,
transform=self,
source=[cz, rz, sqrt_x, reset, meas_z, move],
distance=self.distance,
code_cycle_time=code_cycle_time,
),
)
Loading
Loading