|
| 1 | +# Copyright 2022 The Cirq Developers |
| 2 | +# |
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +# you may not use this file except in compliance with the License. |
| 5 | +# You may obtain a copy of the License at |
| 6 | +# |
| 7 | +# https://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +# |
| 9 | +# Unless required by applicable law or agreed to in writing, software |
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +# See the License for the specific language governing permissions and |
| 13 | +# limitations under the License. |
| 14 | +import itertools |
| 15 | +from typing import Tuple, Set, Sequence |
| 16 | + |
| 17 | +import cirq |
| 18 | + |
| 19 | + |
| 20 | +def rotated_surface_code_memory_z_cycle( |
| 21 | + data_qubits: Set[cirq.Qid], |
| 22 | + z_measure_qubits: Set[cirq.Qid], |
| 23 | + x_measure_qubits: Set[cirq.Qid], |
| 24 | + z_order: Sequence[Tuple[int, int]], |
| 25 | + x_order: Sequence[Tuple[int, int]], |
| 26 | +) -> cirq.Circuit: |
| 27 | + """Constructs a circuit for a single round of rotated memory Z surface code. |
| 28 | +
|
| 29 | + Args: |
| 30 | + data_qubits: data qubits for the surface code patch. |
| 31 | + z_measure_qubits: measure qubits to measure Z stabilizers for surface code patch. |
| 32 | + x_measure_qubits: measure qubits to measure X stabilizers for surface code patch. |
| 33 | + z_order: Specifies the order in which the 2/4 data qubit neighbours of a Z measure qubit |
| 34 | + should be processed. |
| 35 | + x_order: Specifies the order in which the 2/4 data qubit neighbours of a X measure qubit |
| 36 | + should be processed. |
| 37 | +
|
| 38 | + Returns: |
| 39 | + A `cirq.Circuit` for a single round of rotated memory Z surface code cycle. |
| 40 | + """ |
| 41 | + |
| 42 | + circuit = cirq.Circuit() |
| 43 | + circuit += cirq.Moment([cirq.H(q) for q in x_measure_qubits]) |
| 44 | + for k in range(4): |
| 45 | + op_list = [] |
| 46 | + for measure_qubits, add, is_x in [ |
| 47 | + [x_measure_qubits, x_order[k], True], |
| 48 | + [z_measure_qubits, z_order[k], False], |
| 49 | + ]: |
| 50 | + for q_meas in measure_qubits: |
| 51 | + q_data = q_meas + add |
| 52 | + if q_data in data_qubits: |
| 53 | + op_list.append(cirq.CNOT(q_meas, q_data) if is_x else cirq.CNOT(q_data, q_meas)) |
| 54 | + circuit += cirq.Moment(op_list) |
| 55 | + circuit += cirq.Moment([cirq.H(q) for q in x_measure_qubits]) |
| 56 | + circuit += cirq.Moment(cirq.measure_each(*x_measure_qubits, *z_measure_qubits)) |
| 57 | + return circuit |
| 58 | + |
| 59 | + |
| 60 | +def surface_code_circuit( |
| 61 | + distance: int, num_rounds: int, moment_by_moment: bool = True |
| 62 | +) -> cirq.Circuit: |
| 63 | + """Constructs a rotated memory Z surface code circuit with `distance` and `num_rounds`. |
| 64 | +
|
| 65 | + The circuit has `dxd` data qubits and `d ** 2 - 1` measure qubits, where `d` is the distance |
| 66 | + of surface code. For more details on rotated surface codes and qubit indexing, see figure 13 |
| 67 | + https://arxiv.org/abs/1111.4022. |
| 68 | +
|
| 69 | + Args: |
| 70 | + distance: Distance of the surface code. |
| 71 | + num_rounds: Number of error correction rounds for memory Z experiment. |
| 72 | + moment_by_moment: If True, the circuit is constructed moment-by-moment instead of |
| 73 | + operation-by-operation. This is useful to benchmark different circuit construction |
| 74 | + patterns for the same circuit. |
| 75 | +
|
| 76 | + Returns: |
| 77 | + A `cirq.Circuit` for surface code memory Z experiment for `distance` and `num_rounds`. |
| 78 | + """ |
| 79 | + |
| 80 | + def ndrange(*ranges: Tuple[int, ...]): |
| 81 | + return itertools.product(*[range(*r) for r in ranges]) |
| 82 | + |
| 83 | + data_qubits = {cirq.q(2 * x + 1, 2 * y + 1) for x, y in ndrange((distance,), (distance,))} |
| 84 | + z_measure_qubits = { |
| 85 | + cirq.q(2 * x, 2 * y) for x, y in ndrange((1, distance), (distance + 1,)) if x % 2 != y % 2 |
| 86 | + } |
| 87 | + x_measure_qubits = { |
| 88 | + cirq.q(2 * x, 2 * y) for x, y in ndrange((distance + 1,), (1, distance)) if x % 2 == y % 2 |
| 89 | + } |
| 90 | + x_order = [(1, 1), (1, -1), (-1, 1), (-1, -1)] |
| 91 | + z_order = [(1, 1), (-1, 1), (1, -1), (-1, -1)] |
| 92 | + surface_code_cycle = rotated_surface_code_memory_z_cycle( |
| 93 | + data_qubits, x_measure_qubits, z_measure_qubits, x_order, z_order |
| 94 | + ) |
| 95 | + if moment_by_moment: |
| 96 | + return cirq.Circuit( |
| 97 | + surface_code_cycle * num_rounds, cirq.Moment(cirq.measure_each(*data_qubits)) |
| 98 | + ) |
| 99 | + else: |
| 100 | + return cirq.Circuit( |
| 101 | + [*surface_code_cycle.all_operations()] * num_rounds, cirq.measure_each(*data_qubits) |
| 102 | + ) |
| 103 | + |
| 104 | + |
| 105 | +class SurfaceCodeRotatedMemoryZ: |
| 106 | + pretty_name = "Surface Code Rotated Memory-Z Benchmarks." |
| 107 | + params = [*range(3, 26, 2)] |
| 108 | + param_names = ["distance"] |
| 109 | + |
| 110 | + def time_circuit_construction_moment_by_moment(self, distance: int) -> None: |
| 111 | + """Benchmark circuit construction for Rotated Bottom-Z Surface code.""" |
| 112 | + _ = surface_code_circuit(distance, distance * distance) |
| 113 | + |
| 114 | + def time_circuit_construction_operations_by_operation(self, distance: int) -> None: |
| 115 | + """Benchmark circuit construction for Rotated Bottom-Z Surface code.""" |
| 116 | + _ = surface_code_circuit(distance, distance * distance, False) |
| 117 | + |
| 118 | + def track_circuit_operation_count(self, distance: int) -> int: |
| 119 | + """Benchmark operation count for Rotated Bottom-Z Surface code.""" |
| 120 | + circuit = surface_code_circuit(distance, distance * distance) |
| 121 | + return sum(1 for _ in circuit.all_operations()) |
| 122 | + |
| 123 | + def track_circuit_depth(self, distance: int) -> int: |
| 124 | + """Benchmark operation count for Rotated Bottom-Z Surface code.""" |
| 125 | + circuit = surface_code_circuit(distance, distance * distance) |
| 126 | + return len(circuit) |
| 127 | + |
| 128 | + |
| 129 | +class XOnAllQubitsCircuit: |
| 130 | + pretty_name = "N * D times X gate on all qubits." |
| 131 | + params = [[1, 10, 100, 1000], [1, 10, 100, 1000]] |
| 132 | + param_names = ["Number of Qubits(N)", "Depth(D)"] |
| 133 | + |
| 134 | + def time_circuit_construction(self, N: int, D: int): |
| 135 | + q = cirq.LineQubit.range(N) |
| 136 | + return cirq.Circuit(cirq.Moment(cirq.X.on_each(*q)) for _ in range(D)) |
0 commit comments