Skip to content

Commit

Permalink
Improve method for merging QEC cycle circuits (#133)
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcSerraPeralta authored Nov 1, 2024
1 parent 58df7e3 commit 8fe8fce
Show file tree
Hide file tree
Showing 8 changed files with 537 additions and 218 deletions.
128 changes: 80 additions & 48 deletions surface_sim/circuit_blocks/rot_surface_code_css.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from collections.abc import Iterator, Collection
from itertools import chain

from stim import Circuit
Expand All @@ -15,6 +16,7 @@
"log_x",
"log_z",
"qec_round",
"qec_round_iterator",
"init_qubits",
"log_trans_s",
]
Expand All @@ -25,7 +27,7 @@ def qec_round(
layout: Layout,
detectors: Detectors,
anc_reset: bool = True,
anc_detectors: list[str] | None = None,
anc_detectors: Collection[str] | None = None,
) -> Circuit:
"""
Returns stim circuit corresponding to a QEC cycle
Expand All @@ -38,7 +40,7 @@ def qec_round(
layout
Code layout.
detectors
Detector definitions to use.
Detector object to use for their definition.
anc_reset
If ``True``, ancillas are reset at the beginning of the QEC cycle.
By default ``True``.
Expand All @@ -51,16 +53,56 @@ def qec_round(
-----
This implementation follows:
https://doi.org/10.1103/PhysRevApplied.8.034021
"""
circuit = sum(
qec_round_iterator(model=model, layout=layout, anc_reset=anc_reset),
start=Circuit(),
)

# add detectors
anc_qubits = layout.get_qubits(role="anc")
if anc_detectors is None:
anc_detectors = anc_qubits
if set(anc_detectors) > set(anc_qubits):
raise ValueError("Elements in 'anc_detectors' are not ancilla qubits.")

circuit += detectors.build_from_anc(
model.meas_target, anc_reset, anc_qubits=anc_detectors
)

return circuit


def qec_round_iterator(
model: Model,
layout: Layout,
anc_reset: bool = True,
) -> Iterator[Circuit]:
"""
Yields stim circuit blocks which as a whole correspond to a QEC cycle
of the given model without the detectors.
Parameters
----------
model
Noise model for the gates.
layout
Code layout.
anc_reset
If ``True``, ancillas are reset at the beginning of the QEC cycle.
By default ``True``.
Notes
-----
This implementation follows:
https://doi.org/10.1103/PhysRevApplied.8.034021
"""
if layout.code != "rotated_surface_code":
raise TypeError(
"The given layout is not a rotated surface code, " f"but a {layout.code}"
)
if anc_detectors is None:
anc_detectors = layout.get_qubits(role="anc")
if set(anc_detectors) > set(layout.get_qubits(role="anc")):
raise ValueError("Some of the given 'anc_qubits' are not ancilla qubits.")

data_qubits = layout.get_qubits(role="data")
anc_qubits = layout.get_qubits(role="anc")
Expand All @@ -70,15 +112,13 @@ def qec_round(
stab_types = list(int_order.keys())
x_stabs = layout.get_qubits(role="anc", stab_type="x_type")

circuit = Circuit()

circuit += model.incoming_noise(data_qubits)
circuit += model.tick()
yield model.incoming_noise(data_qubits)
yield model.tick()

if anc_reset:
circuit += model.reset(anc_qubits)
circuit += model.idle(data_qubits)
circuit += model.tick()
yield model.reset(anc_qubits)
yield model.idle(data_qubits)
yield model.tick()

# a
directions = [int_order["x_type"][0], int_order["x_type"][3]]
Expand All @@ -87,9 +127,9 @@ def qec_round(
rot_qubits.update(layout.get_neighbors(x_stabs, direction=directions[1]))
idle_qubits = qubits - rot_qubits

circuit += model.hadamard(rot_qubits)
circuit += model.idle(idle_qubits)
circuit += model.tick()
yield model.hadamard(rot_qubits)
yield model.idle(idle_qubits)
yield model.tick()

# b
interacted_qubits = set()
Expand All @@ -100,16 +140,16 @@ def qec_round(
int_qubits = list(chain.from_iterable(int_pairs))
interacted_qubits.update(int_qubits)

circuit += model.cphase(int_qubits)
yield model.cphase(int_qubits)

idle_qubits = qubits - set(interacted_qubits)
circuit += model.idle(idle_qubits)
circuit += model.tick()
yield model.idle(idle_qubits)
yield model.tick()

# c
circuit += model.hadamard(data_qubits)
circuit += model.idle(anc_qubits)
circuit += model.tick()
yield model.hadamard(data_qubits)
yield model.idle(anc_qubits)
yield model.tick()

# d
interacted_qubits = set()
Expand All @@ -120,11 +160,11 @@ def qec_round(
int_qubits = list(chain.from_iterable(int_pairs))
interacted_qubits.update(int_qubits)

circuit += model.cphase(int_qubits)
yield model.cphase(int_qubits)

idle_qubits = qubits - set(interacted_qubits)
circuit += model.idle(idle_qubits)
circuit += model.tick()
yield model.idle(idle_qubits)
yield model.tick()

# e
interacted_qubits = set()
Expand All @@ -135,16 +175,16 @@ def qec_round(
int_qubits = list(chain.from_iterable(int_pairs))
interacted_qubits.update(int_qubits)

circuit += model.cphase(int_qubits)
yield model.cphase(int_qubits)

idle_qubits = qubits - set(interacted_qubits)
circuit += model.idle(idle_qubits)
circuit += model.tick()
yield model.idle(idle_qubits)
yield model.tick()

# f
circuit += model.hadamard(data_qubits)
circuit += model.idle(anc_qubits)
circuit += model.tick()
yield model.hadamard(data_qubits)
yield model.idle(anc_qubits)
yield model.tick()

# g
interacted_qubits = set()
Expand All @@ -155,11 +195,11 @@ def qec_round(
int_qubits = list(chain.from_iterable(int_pairs))
interacted_qubits.update(int_qubits)

circuit += model.cphase(int_qubits)
yield model.cphase(int_qubits)

idle_qubits = qubits - set(interacted_qubits)
circuit += model.idle(idle_qubits)
circuit += model.tick()
yield model.idle(idle_qubits)
yield model.tick()

# h
directions = [int_order["x_type"][0], int_order["x_type"][3]]
Expand All @@ -168,19 +208,11 @@ def qec_round(
rot_qubits.update(layout.get_neighbors(x_stabs, direction=directions[1]))
idle_qubits = qubits - rot_qubits

circuit += model.hadamard(rot_qubits)
circuit += model.idle(idle_qubits)
circuit += model.tick()
yield model.hadamard(rot_qubits)
yield model.idle(idle_qubits)
yield model.tick()

# i
circuit += model.measure(anc_qubits)
circuit += model.idle(data_qubits)
circuit += model.tick()

# add detectors
detectors_stim = detectors.build_from_anc(
model.meas_target, anc_reset, anc_qubits=anc_detectors
)
circuit += detectors_stim

return circuit
yield model.measure(anc_qubits)
yield model.idle(data_qubits)
yield model.tick()
100 changes: 66 additions & 34 deletions surface_sim/circuit_blocks/rot_surface_code_css_pipelined.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from collections.abc import Iterator, Collection
from itertools import chain

from stim import Circuit
Expand All @@ -15,6 +16,7 @@
"log_x",
"log_z",
"qec_round",
"qec_round_iterator",
"init_qubits",
"log_trans_s",
]
Expand All @@ -25,7 +27,7 @@ def qec_round(
layout: Layout,
detectors: Detectors,
anc_reset: bool = True,
anc_detectors: list[str] | None = None,
anc_detectors: Collection[str] | None = None,
) -> Circuit:
"""
Returns stim circuit corresponding to a QEC cycle
Expand All @@ -38,23 +40,63 @@ def qec_round(
layout
Code layout.
detectors
Detector definitions to use.
Detector object to use for their definition.
anc_reset
If ``True``, ancillas are reset at the beginning of the QEC cycle.
By default ``True``.
anc_detectors
List of ancilla qubits for which to define the detectors.
If ``None``, adds all detectors.
By default ``None``.
Notes
-----
This implementation follows:
https://doi.org/10.1103/PhysRevApplied.8.034021
"""
circuit = sum(
qec_round_iterator(model=model, layout=layout, anc_reset=anc_reset),
start=Circuit(),
)

# add detectors
anc_qubits = layout.get_qubits(role="anc")
if anc_detectors is None:
anc_detectors = anc_qubits
if set(anc_detectors) > set(anc_qubits):
raise ValueError("Elements in 'anc_detectors' are not ancilla qubits.")

circuit += detectors.build_from_anc(
model.meas_target, anc_reset, anc_qubits=anc_detectors
)

return circuit


def qec_round_iterator(
model: Model,
layout: Layout,
anc_reset: bool = True,
) -> Iterator[Circuit]:
"""
Yields stim circuit blocks which as a whole correspond to a QEC cycle
of the given model without the detectors.
Parameters
----------
model
Noise model for the gates.
layout
Code layout.
anc_reset
If ``True``, ancillas are reset at the beginning of the QEC cycle.
By default ``True``.
"""
if layout.code != "rotated_surface_code":
raise TypeError(
"The given layout is not a rotated surface code, " f"but a {layout.code}"
)
if anc_detectors is None:
anc_detectors = layout.get_qubits(role="anc")
if set(anc_detectors) > set(layout.get_qubits(role="anc")):
raise ValueError("Some of the given 'anc_qubits' are not ancilla qubits.")

data_qubits = layout.get_qubits(role="data")
anc_qubits = layout.get_qubits(role="anc")
Expand All @@ -63,15 +105,13 @@ def qec_round(
int_order = layout.interaction_order
stab_types = list(int_order.keys())

circuit = Circuit()

circuit += model.incoming_noise(data_qubits)
circuit += model.tick()
yield model.incoming_noise(data_qubits)
yield model.tick()

if anc_reset:
circuit += model.reset(anc_qubits)
circuit += model.idle(data_qubits)
circuit += model.tick()
yield model.reset(anc_qubits)
yield model.idle(data_qubits)
yield model.tick()

for ind, stab_type in enumerate(stab_types):
stab_qubits = layout.get_qubits(role="anc", stab_type=stab_type)
Expand All @@ -81,9 +121,9 @@ def qec_round(

if not ind:
idle_qubits = qubits - rot_qubits
circuit += model.hadamard(rot_qubits)
circuit += model.idle(idle_qubits)
circuit += model.tick()
yield model.hadamard(rot_qubits)
yield model.idle(idle_qubits)
yield model.tick()

for ord_dir in int_order[stab_type]:
int_pairs = layout.get_neighbors(
Expand All @@ -92,27 +132,19 @@ def qec_round(
int_qubits = list(chain.from_iterable(int_pairs))
idle_qubits = qubits - set(int_qubits)

circuit += model.cphase(int_qubits)
circuit += model.idle(idle_qubits)
circuit += model.tick()
yield model.cphase(int_qubits)
yield model.idle(idle_qubits)
yield model.tick()

if not ind:
circuit += model.hadamard(qubits)
yield model.hadamard(qubits)
else:
idle_qubits = qubits - rot_qubits
circuit += model.hadamard(rot_qubits)
circuit += model.idle(idle_qubits)
yield model.hadamard(rot_qubits)
yield model.idle(idle_qubits)

circuit += model.tick()
yield model.tick()

circuit += model.measure(anc_qubits)
circuit += model.idle(data_qubits)
circuit += model.tick()

# add detectors
detectors_stim = detectors.build_from_anc(
model.meas_target, anc_reset, anc_qubits=anc_detectors
)
circuit += detectors_stim

return circuit
yield model.measure(anc_qubits)
yield model.idle(data_qubits)
yield model.tick()
Loading

0 comments on commit 8fe8fce

Please sign in to comment.