From 1a54dfbeeb03c7cad0fb7940266965ffa9cf34d5 Mon Sep 17 00:00:00 2001 From: Adrien Suau Date: Wed, 28 Feb 2024 09:10:28 +0100 Subject: [PATCH] Refactor Plaquette hierarchy (#137) This PR addresses #136. --------- Co-authored-by: Yiming Zhang <61700160+inmzhang@users.noreply.github.com> --- ...cal_qubit_extended_memory_experiment.ipynb | 117 ++++-- .../logical_qubit_memory_experiment.ipynb | 118 ++++-- notebooks/move_qubit_along_a_line.ipynb | 373 +++++++++--------- src/tqec/__init__.py | 1 - src/tqec/detectors/measurement_map.py | 18 +- src/tqec/detectors/operation.py | 9 + src/tqec/enums.py | 17 + src/tqec/generation/circuit_test.py | 167 +++++--- src/tqec/plaquette/__init__.py | 7 +- src/tqec/plaquette/library/__init__.py | 40 +- src/tqec/plaquette/library/empty.py | 14 + src/tqec/plaquette/library/initialisation.py | 74 ++++ src/tqec/plaquette/library/measurement.py | 61 +++ src/tqec/plaquette/library/utils/detectors.py | 26 ++ src/tqec/plaquette/library/utils/pauli.py | 77 ++++ src/tqec/plaquette/library/xx.py | 180 +-------- src/tqec/plaquette/library/xxxx.py | 149 +------ src/tqec/plaquette/library/zz.py | 162 +------- src/tqec/plaquette/library/zzzz.py | 146 +------ src/tqec/plaquette/plaquette.py | 78 ++-- src/tqec/templates/atomic/rectangle.py | 11 +- src/tqec/templates/atomic/square.py | 2 +- src/tqec/templates/base.py | 11 +- 23 files changed, 879 insertions(+), 979 deletions(-) create mode 100644 src/tqec/plaquette/library/empty.py create mode 100644 src/tqec/plaquette/library/initialisation.py create mode 100644 src/tqec/plaquette/library/measurement.py create mode 100644 src/tqec/plaquette/library/utils/detectors.py create mode 100644 src/tqec/plaquette/library/utils/pauli.py diff --git a/notebooks/logical_qubit_extended_memory_experiment.ipynb b/notebooks/logical_qubit_extended_memory_experiment.ipynb index 9dc70677..a5e484ed 100644 --- a/notebooks/logical_qubit_extended_memory_experiment.ipynb +++ b/notebooks/logical_qubit_extended_memory_experiment.ipynb @@ -41,14 +41,17 @@ " AfterResetFlipNoise,\n", " BeforeMeasurementFlipNoise,\n", ")\n", - "from tqec.plaquette.plaquette import PlaquetteList\n", + "from tqec.plaquette.plaquette import Plaquette\n", "from tqec.plaquette.library import (\n", - " XXPlaquetteList,\n", - " XXXXPlaquetteList,\n", - " ZZPlaquetteList,\n", - " ZZZZPlaquetteList,\n", + " ZSquareInitialisationPlaquette,\n", + " ZRoundedInitialisationPlaquette,\n", + " XXMemoryPlaquette,\n", + " XXXXMemoryPlaquette,\n", + " ZZMemoryPlaquette,\n", + " ZZZZMemoryPlaquette,\n", + " MeasurementRoundedPlaquette,\n", + " MeasurementSquarePlaquette,\n", ")\n", - "from tqec.position import Shape2D\n", "from tqec.display import display_template" ] }, @@ -129,7 +132,6 @@ " Dimension(k_width, LinearFunction(2)),\n", " Dimension(k_height, LinearFunction(2)),\n", " )\n", - "\n", " # 2. Define the different plaquettes that will be used on the template defined above.\n", " # As noted in the text description above this function, the plaquette instances in the list below\n", " # implement by default the memory experiment we want to perform. That is the reason why there is no\n", @@ -140,17 +142,63 @@ " # - CNOTs indices are 3, 4, 5, 6\n", " # - (H gate for X-stabilizers is 7)\n", " # - Measurement index is 8\n", - " plaquettes: list[PlaquetteList] = [\n", - " XXPlaquetteList(\n", - " PlaquetteOrientation.UP, [1, 2, 5, 6, 7, 8], include_detector=False\n", - " ),\n", - " ZZPlaquetteList(PlaquetteOrientation.LEFT, [1, 5, 6, 8]),\n", - " XXXXPlaquetteList([1, 2, 3, 4, 5, 6, 7, 8], include_detector=False),\n", - " ZZZZPlaquetteList([1, 3, 4, 5, 6, 8]),\n", - " ZZPlaquetteList(PlaquetteOrientation.RIGHT, [1, 3, 4, 8]),\n", - " XXPlaquetteList(\n", - " PlaquetteOrientation.DOWN, [1, 2, 3, 4, 7, 8], include_detector=False\n", - " ),\n", + " plaquettes: list[list[Plaquette]] = [\n", + " [\n", + " ZRoundedInitialisationPlaquette(PlaquetteOrientation.UP),\n", + " XXMemoryPlaquette(\n", + " PlaquetteOrientation.UP,\n", + " [1, 2, 5, 6, 7, 8],\n", + " include_detector=False,\n", + " is_first_round=True,\n", + " ),\n", + " XXMemoryPlaquette(PlaquetteOrientation.UP, [1, 2, 5, 6, 7, 8]),\n", + " MeasurementRoundedPlaquette(\n", + " PlaquetteOrientation.UP, include_detector=False\n", + " ),\n", + " ],\n", + " [\n", + " ZRoundedInitialisationPlaquette(PlaquetteOrientation.LEFT),\n", + " ZZMemoryPlaquette(\n", + " PlaquetteOrientation.LEFT, [1, 5, 6, 8], is_first_round=True\n", + " ),\n", + " ZZMemoryPlaquette(PlaquetteOrientation.LEFT, [1, 5, 6, 8]),\n", + " MeasurementRoundedPlaquette(PlaquetteOrientation.LEFT),\n", + " ],\n", + " [\n", + " ZSquareInitialisationPlaquette(),\n", + " XXXXMemoryPlaquette(\n", + " [1, 2, 3, 4, 5, 6, 7, 8], include_detector=False, is_first_round=True\n", + " ),\n", + " XXXXMemoryPlaquette([1, 2, 3, 4, 5, 6, 7, 8]),\n", + " MeasurementSquarePlaquette(include_detector=False),\n", + " ],\n", + " [\n", + " ZSquareInitialisationPlaquette(),\n", + " ZZZZMemoryPlaquette([1, 3, 4, 5, 6, 8], is_first_round=True),\n", + " ZZZZMemoryPlaquette([1, 3, 4, 5, 6, 8]),\n", + " MeasurementSquarePlaquette(),\n", + " ],\n", + " [\n", + " ZRoundedInitialisationPlaquette(PlaquetteOrientation.RIGHT),\n", + " ZZMemoryPlaquette(\n", + " PlaquetteOrientation.RIGHT, [1, 3, 4, 8], is_first_round=True\n", + " ),\n", + " ZZMemoryPlaquette(PlaquetteOrientation.RIGHT, [1, 3, 4, 8]),\n", + " MeasurementRoundedPlaquette(PlaquetteOrientation.RIGHT),\n", + " ],\n", + " [\n", + " ZRoundedInitialisationPlaquette(PlaquetteOrientation.DOWN),\n", + " XXMemoryPlaquette(\n", + " PlaquetteOrientation.DOWN,\n", + " [1, 2, 3, 4, 7, 8],\n", + " include_detector=False,\n", + " is_first_round=True,\n", + " ),\n", + " XXMemoryPlaquette(PlaquetteOrientation.DOWN, [1, 2, 3, 4, 7, 8]),\n", + " MeasurementRoundedPlaquette(\n", + " PlaquetteOrientation.DOWN, include_detector=False\n", + " ),\n", + " ],\n", " ]\n", "\n", " # 3. Define the layer modifiers.\n", @@ -160,48 +208,39 @@ " # cirq.CircuitOperation that natively supports repeting an operation efficiently, and that is\n", " # natively recognized by the `tqec` library.\n", " def make_repeated_layer(circuit: cirq.Circuit) -> cirq.Circuit:\n", - " # Note: we do not care on which qubit it is applied, but we want a SHIFT_COORDS instruction\n", - " # to be inserted somewhere in the repetition loop. It is inserted at the beginning.\n", - " circuit_to_repeat = cirq.Circuit([make_shift_coords(0, 0, 1)]) + circuit\n", + " circuit_to_repeat = circuit + cirq.Circuit(\n", + " cirq.Moment(make_shift_coords(0, 0, 1))\n", + " )\n", " repeated_circuit_operation = cirq.CircuitOperation(\n", " circuit_to_repeat.freeze()\n", " ).repeat(repetitions)\n", " return cirq.Circuit([repeated_circuit_operation])\n", "\n", - " layer_modificators = {1: make_repeated_layer}\n", + " layer_modificators = {2: make_repeated_layer}\n", "\n", " # 4. Actually create the cirq.Circuit instance by concatenating the circuits generated\n", " # for each layer and potentially modified by the modifiers defined above.\n", " circuit = cirq.Circuit()\n", - " for layer_index in range(3):\n", + " for layer_index in range(4):\n", " layer_circuit = generate_circuit(\n", " template,\n", - " [plaquette_list.plaquettes[layer_index] for plaquette_list in plaquettes],\n", + " [plaquette_list[layer_index] for plaquette_list in plaquettes],\n", " )\n", " layer_circuit = normalise_circuit(layer_circuit)\n", " circuit += layer_modificators.get(layer_index, lambda circ: circ)(layer_circuit)\n", + " circuit += cirq.Moment(make_shift_coords(0, 0, 1))\n", "\n", " # 5. Define the observable.\n", " # The observable is defined and added to the cirq.Circuit instance just here.\n", - " # We assume that each plaquette has the same shape (i.e., needs the same number of qubits on the X and\n", - " # Y dimensions). XX and ZZ stabilizers have been artificially made 3x3 plaquettes for this purpose. This\n", - " # assumption will eventually need to be lifted.\n", - " plaquette_shape: Shape2D = plaquettes[0].plaquettes[0].shape\n", - " assert all(\n", - " p.shape == plaquette_shape\n", - " for plaquette_list in plaquettes\n", - " for p in plaquette_list.plaquettes\n", - " ), \"All plaquettes should have exactly the same shape for the moment.\"\n", - " origin = cirq.GridQubit(\n", - " (plaquette_shape.y - 1) * (1 + 2 * k_height // 2), plaquette_shape.x - 1\n", - " )\n", + " increments = template.get_increments()\n", + " origin = cirq.GridQubit(increments.y * k_width + 1, 1)\n", " circuit.append(\n", " cirq.Moment(\n", " make_observable(\n", " origin,\n", " [\n", - " (cirq.GridQubit(0, i * (plaquette_shape.x - 1)), -1)\n", - " for i in range(2 * k_width + 1)\n", + " (cirq.GridQubit(0, i * increments.x), -1)\n", + " for i in range(0, template.shape.x - 1)\n", " ],\n", " )\n", " )\n", @@ -341,7 +380,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" + "version": "3.12.0" } }, "nbformat": 4, diff --git a/notebooks/logical_qubit_memory_experiment.ipynb b/notebooks/logical_qubit_memory_experiment.ipynb index a9ac4599..39707a47 100644 --- a/notebooks/logical_qubit_memory_experiment.ipynb +++ b/notebooks/logical_qubit_memory_experiment.ipynb @@ -39,14 +39,17 @@ " AfterResetFlipNoise,\n", " BeforeMeasurementFlipNoise,\n", ")\n", - "from tqec.plaquette.plaquette import PlaquetteList\n", + "from tqec.plaquette.plaquette import Plaquette\n", "from tqec.plaquette.library import (\n", - " XXPlaquetteList,\n", - " XXXXPlaquetteList,\n", - " ZZPlaquetteList,\n", - " ZZZZPlaquetteList,\n", - ")\n", - "from tqec.position import Shape2D" + " ZSquareInitialisationPlaquette,\n", + " ZRoundedInitialisationPlaquette,\n", + " XXMemoryPlaquette,\n", + " XXXXMemoryPlaquette,\n", + " ZZMemoryPlaquette,\n", + " ZZZZMemoryPlaquette,\n", + " MeasurementSquarePlaquette,\n", + " MeasurementRoundedPlaquette,\n", + ")" ] }, { @@ -121,17 +124,63 @@ " # - CNOTs indices are 3, 4, 5, 6\n", " # - (H gate for X-stabilizers is 7)\n", " # - Measurement index is 8\n", - " plaquettes: list[PlaquetteList] = [\n", - " XXPlaquetteList(\n", - " PlaquetteOrientation.UP, [1, 2, 5, 6, 7, 8], include_detector=False\n", - " ),\n", - " ZZPlaquetteList(PlaquetteOrientation.LEFT, [1, 5, 6, 8]),\n", - " XXXXPlaquetteList([1, 2, 3, 4, 5, 6, 7, 8], include_detector=False),\n", - " ZZZZPlaquetteList([1, 3, 4, 5, 6, 8]),\n", - " ZZPlaquetteList(PlaquetteOrientation.RIGHT, [1, 3, 4, 8]),\n", - " XXPlaquetteList(\n", - " PlaquetteOrientation.DOWN, [1, 2, 3, 4, 7, 8], include_detector=False\n", - " ),\n", + " plaquettes: list[list[Plaquette]] = [\n", + " [\n", + " ZRoundedInitialisationPlaquette(PlaquetteOrientation.UP),\n", + " XXMemoryPlaquette(\n", + " PlaquetteOrientation.UP,\n", + " [1, 2, 5, 6, 7, 8],\n", + " include_detector=False,\n", + " is_first_round=True,\n", + " ),\n", + " XXMemoryPlaquette(PlaquetteOrientation.UP, [1, 2, 5, 6, 7, 8]),\n", + " MeasurementRoundedPlaquette(\n", + " PlaquetteOrientation.UP, include_detector=False\n", + " ),\n", + " ],\n", + " [\n", + " ZRoundedInitialisationPlaquette(PlaquetteOrientation.LEFT),\n", + " ZZMemoryPlaquette(\n", + " PlaquetteOrientation.LEFT, [1, 5, 6, 8], is_first_round=True\n", + " ),\n", + " ZZMemoryPlaquette(PlaquetteOrientation.LEFT, [1, 5, 6, 8]),\n", + " MeasurementRoundedPlaquette(PlaquetteOrientation.LEFT),\n", + " ],\n", + " [\n", + " ZSquareInitialisationPlaquette(),\n", + " XXXXMemoryPlaquette(\n", + " [1, 2, 3, 4, 5, 6, 7, 8], include_detector=False, is_first_round=True\n", + " ),\n", + " XXXXMemoryPlaquette([1, 2, 3, 4, 5, 6, 7, 8]),\n", + " MeasurementSquarePlaquette(include_detector=False),\n", + " ],\n", + " [\n", + " ZSquareInitialisationPlaquette(),\n", + " ZZZZMemoryPlaquette([1, 3, 4, 5, 6, 8], is_first_round=True),\n", + " ZZZZMemoryPlaquette([1, 3, 4, 5, 6, 8]),\n", + " MeasurementSquarePlaquette(),\n", + " ],\n", + " [\n", + " ZRoundedInitialisationPlaquette(PlaquetteOrientation.RIGHT),\n", + " ZZMemoryPlaquette(\n", + " PlaquetteOrientation.RIGHT, [1, 3, 4, 8], is_first_round=True\n", + " ),\n", + " ZZMemoryPlaquette(PlaquetteOrientation.RIGHT, [1, 3, 4, 8]),\n", + " MeasurementRoundedPlaquette(PlaquetteOrientation.RIGHT),\n", + " ],\n", + " [\n", + " ZRoundedInitialisationPlaquette(PlaquetteOrientation.DOWN),\n", + " XXMemoryPlaquette(\n", + " PlaquetteOrientation.DOWN,\n", + " [1, 2, 3, 4, 7, 8],\n", + " include_detector=False,\n", + " is_first_round=True,\n", + " ),\n", + " XXMemoryPlaquette(PlaquetteOrientation.DOWN, [1, 2, 3, 4, 7, 8]),\n", + " MeasurementRoundedPlaquette(\n", + " PlaquetteOrientation.DOWN, include_detector=False\n", + " ),\n", + " ],\n", " ]\n", "\n", " # 3. Define the layer modifiers.\n", @@ -141,50 +190,39 @@ " # cirq.CircuitOperation that natively supports repeting an operation efficiently, and that is\n", " # natively recognized by the `tqec` library.\n", " def make_repeated_layer(circuit: cirq.Circuit) -> cirq.Circuit:\n", - " # Note: we do not care on which qubit it is applied, but we want a SHIFT_COORDS instruction\n", - " # to be inserted somewhere in the repetition loop. It is inserted at the beginning.\n", - " any_qubit = next(iter(circuit.all_qubits()), None)\n", - " assert (\n", - " any_qubit is not None\n", - " ), \"Could not find any qubit in the given Circuit instance.\"\n", - " circuit_to_repeat = cirq.Circuit([make_shift_coords(0, 0, 1)]) + circuit\n", + " circuit_to_repeat = circuit + cirq.Circuit(\n", + " cirq.Moment(make_shift_coords(0, 0, 1))\n", + " )\n", " repeated_circuit_operation = cirq.CircuitOperation(\n", " circuit_to_repeat.freeze()\n", " ).repeat(repetitions)\n", " return cirq.Circuit([repeated_circuit_operation])\n", "\n", - " layer_modificators = {1: make_repeated_layer}\n", + " layer_modificators = {2: make_repeated_layer}\n", "\n", " # 4. Actually create the cirq.Circuit instance by concatenating the circuits generated\n", " # for each layer and potentially modified by the modifiers defined above.\n", " circuit = cirq.Circuit()\n", - " for layer_index in range(3):\n", + " for layer_index in range(4):\n", " layer_circuit = generate_circuit(\n", " template,\n", - " [plaquette_list.plaquettes[layer_index] for plaquette_list in plaquettes],\n", + " [plaquette_list[layer_index] for plaquette_list in plaquettes],\n", " )\n", " layer_circuit = normalise_circuit(layer_circuit)\n", " circuit += layer_modificators.get(layer_index, lambda circ: circ)(layer_circuit)\n", + " circuit += cirq.Moment(make_shift_coords(0, 0, 1))\n", "\n", " # 5. Define the observable.\n", " # The observable is defined and added to the cirq.Circuit instance just here.\n", - " # We assume that each plaquette has the same shape (i.e., needs the same number of qubits on the X and\n", - " # Y dimensions). XX and ZZ stabilizers have been artificially made 3x3 plaquettes for this purpose. This\n", - " # assumption will eventually need to be lifted.\n", - " plaquette_shape: Shape2D = plaquettes[0].plaquettes[0].shape\n", - " assert all(\n", - " p.shape == plaquette_shape\n", - " for plaquette_list in plaquettes\n", - " for p in plaquette_list.plaquettes\n", - " ), \"All plaquettes should have exactly the same shape for the moment.\"\n", - " origin = cirq.GridQubit(plaquette_shape.y - 1, plaquette_shape.x - 1)\n", + " increments = template.get_increments()\n", + " origin = cirq.GridQubit(increments.y * k + 1, 1)\n", " circuit.append(\n", " cirq.Moment(\n", " make_observable(\n", " origin,\n", " [\n", - " (cirq.GridQubit(0, i * (plaquette_shape.x - 1)), -1)\n", - " for i in range(2 * k + 1)\n", + " (cirq.GridQubit(0, i * increments.x), -1)\n", + " for i in range(0, template.shape.x - 1)\n", " ],\n", " )\n", " )\n", diff --git a/notebooks/move_qubit_along_a_line.ipynb b/notebooks/move_qubit_along_a_line.ipynb index 82b7d6d7..13fada35 100644 --- a/notebooks/move_qubit_along_a_line.ipynb +++ b/notebooks/move_qubit_along_a_line.ipynb @@ -73,9 +73,18 @@ " AfterResetFlipNoise,\n", " BeforeMeasurementFlipNoise,\n", ")\n", - "from tqec.plaquette.schedule import ScheduledCircuit\n", - "from tqec.plaquette.plaquette import PlaquetteList\n", - "from tqec.plaquette.library import *\n", + "from tqec.plaquette.library import (\n", + " ZSquareInitialisationPlaquette,\n", + " ZRoundedInitialisationPlaquette,\n", + " XXMemoryPlaquette,\n", + " XXXXMemoryPlaquette,\n", + " ZZMemoryPlaquette,\n", + " ZZZZMemoryPlaquette,\n", + " EmptySquarePlaquette,\n", + " EmptyRoundedPlaquette,\n", + " MeasurementSquarePlaquette,\n", + " MeasurementRoundedPlaquette,\n", + ")\n", "from tqec.display import display_template" ] }, @@ -156,7 +165,7 @@ " self,\n", " plaquette_indices: ty.Sequence[int],\n", " ) -> np.ndarray:\n", - " self._check_plaquette_number(plaquette_indices)\n", + " self._check_plaquette_number(plaquette_indices, 4)\n", " (\n", " x_plaquette,\n", " z_plaquette,\n", @@ -273,193 +282,191 @@ "schedule_xx_up = [1, 2, 5, 6, 7, 8]\n", "schedule_xx_down = [1, 2, 3, 4, 7, 8]\n", "\n", - "dummy_circuit = lambda: ScheduledCircuit(cirq.Circuit())\n", - "\n", "PLAQUETTES = [\n", " # 1\n", - " PlaquetteList(\n", - " [\n", - " XXXXInitialisationPlaquette(schedule_xxxx, include_detector=False),\n", - " XXXXMemoryPlaquette(schedule_xxxx),\n", - " XXXXMemoryPlaquette(schedule_xxxx),\n", - " XXXXMemoryPlaquette(schedule_xxxx),\n", - " XXXXFinalMeasurementPlaquette(include_detector=False),\n", - " BaseXXXXPlaquette(dummy_circuit()),\n", - " BaseXXXXPlaquette(dummy_circuit()),\n", - " ]\n", - " ),\n", + " [\n", + " ZSquareInitialisationPlaquette(),\n", + " XXXXMemoryPlaquette(schedule_xxxx, include_detector=False, is_first_round=True),\n", + " XXXXMemoryPlaquette(schedule_xxxx),\n", + " XXXXMemoryPlaquette(schedule_xxxx),\n", + " XXXXMemoryPlaquette(schedule_xxxx),\n", + " MeasurementSquarePlaquette(include_detector=False),\n", + " EmptySquarePlaquette(),\n", + " EmptySquarePlaquette(),\n", + " ],\n", " # 2\n", - " PlaquetteList(\n", - " [\n", - " ZZZZInitialisationPlaquette(schedule_zzzz),\n", - " ZZZZMemoryPlaquette(schedule_zzzz),\n", - " ZZZZMemoryPlaquette(schedule_zzzz),\n", - " ZZZZMemoryPlaquette(schedule_zzzz),\n", - " ZZZZFinalMeasurementPlaquette(),\n", - " BaseZZZZPlaquette(dummy_circuit()),\n", - " BaseZZZZPlaquette(dummy_circuit()),\n", - " ]\n", - " ),\n", + " [\n", + " ZSquareInitialisationPlaquette(),\n", + " ZZZZMemoryPlaquette(schedule_zzzz, is_first_round=True),\n", + " ZZZZMemoryPlaquette(schedule_zzzz),\n", + " ZZZZMemoryPlaquette(schedule_zzzz),\n", + " ZZZZMemoryPlaquette(schedule_zzzz),\n", + " MeasurementSquarePlaquette(),\n", + " EmptySquarePlaquette(),\n", + " EmptySquarePlaquette(),\n", + " ],\n", " # 3\n", - " PlaquetteList(\n", - " [\n", - " ZZZZInitialisationPlaquette(schedule_zzzz),\n", - " ZZZZMemoryPlaquette(schedule_zzzz),\n", - " ZZZZMemoryPlaquette(schedule_zzzz),\n", - " ZZZZMemoryPlaquette(schedule_zzzz),\n", - " BaseZZZZPlaquette(dummy_circuit()),\n", - " ZZMemoryPlaquette(PlaquetteOrientation.LEFT, schedule_zz_left),\n", - " ZZFinalMeasurementPlaquette(PlaquetteOrientation.LEFT),\n", - " ]\n", - " ),\n", + " [\n", + " ZSquareInitialisationPlaquette(),\n", + " ZZZZMemoryPlaquette(schedule_zzzz, is_first_round=True),\n", + " ZZZZMemoryPlaquette(schedule_zzzz),\n", + " ZZZZMemoryPlaquette(schedule_zzzz),\n", + " ZZZZMemoryPlaquette(schedule_zzzz),\n", + " EmptySquarePlaquette(),\n", + " ZZMemoryPlaquette(PlaquetteOrientation.LEFT, schedule_zz_left),\n", + " MeasurementRoundedPlaquette(PlaquetteOrientation.LEFT),\n", + " ],\n", " # 4\n", - " PlaquetteList(\n", - " [\n", - " XXXXInitialisationPlaquette(schedule_xxxx, include_detector=False),\n", - " XXXXMemoryPlaquette(schedule_xxxx),\n", - " XXXXMemoryPlaquette(schedule_xxxx),\n", - " XXXXMemoryPlaquette(schedule_xxxx),\n", - " BaseXXXXPlaquette(dummy_circuit()),\n", - " BaseXXXXPlaquette(dummy_circuit()),\n", - " BaseXXXXPlaquette(dummy_circuit()),\n", - " ]\n", - " ),\n", + " [\n", + " ZSquareInitialisationPlaquette(),\n", + " XXXXMemoryPlaquette(schedule_xxxx, include_detector=False, is_first_round=True),\n", + " XXXXMemoryPlaquette(schedule_xxxx),\n", + " XXXXMemoryPlaquette(schedule_xxxx),\n", + " XXXXMemoryPlaquette(schedule_xxxx),\n", + " EmptySquarePlaquette(),\n", + " EmptySquarePlaquette(),\n", + " EmptySquarePlaquette(),\n", + " ],\n", " # 5\n", - " PlaquetteList(\n", - " [\n", - " XXInitialisationPlaquette(\n", - " PlaquetteOrientation.UP, schedule_xx_up, include_detector=False\n", - " ),\n", - " XXMemoryPlaquette(PlaquetteOrientation.UP, schedule_xx_up),\n", - " XXMemoryPlaquette(PlaquetteOrientation.UP, schedule_xx_up),\n", - " XXMemoryPlaquette(PlaquetteOrientation.UP, schedule_xx_up),\n", - " BaseXXPlaquette(dummy_circuit(), PlaquetteOrientation.UP),\n", - " BaseXXPlaquette(dummy_circuit(), PlaquetteOrientation.UP),\n", - " BaseXXPlaquette(dummy_circuit(), PlaquetteOrientation.UP),\n", - " ]\n", - " ),\n", + " [\n", + " ZRoundedInitialisationPlaquette(PlaquetteOrientation.UP),\n", + " XXMemoryPlaquette(\n", + " PlaquetteOrientation.UP,\n", + " schedule_xx_up,\n", + " include_detector=False,\n", + " is_first_round=True,\n", + " ),\n", + " XXMemoryPlaquette(PlaquetteOrientation.UP, schedule_xx_up),\n", + " XXMemoryPlaquette(PlaquetteOrientation.UP, schedule_xx_up),\n", + " XXMemoryPlaquette(PlaquetteOrientation.UP, schedule_xx_up),\n", + " EmptyRoundedPlaquette(PlaquetteOrientation.UP),\n", + " EmptyRoundedPlaquette(PlaquetteOrientation.UP),\n", + " EmptyRoundedPlaquette(PlaquetteOrientation.UP),\n", + " ],\n", " # 6\n", - " PlaquetteList(\n", - " [\n", - " XXInitialisationPlaquette(\n", - " PlaquetteOrientation.DOWN, schedule_xx_down, include_detector=False\n", - " ),\n", - " XXMemoryPlaquette(PlaquetteOrientation.DOWN, schedule_xx_down),\n", - " XXMemoryPlaquette(PlaquetteOrientation.DOWN, schedule_xx_down),\n", - " XXMemoryPlaquette(PlaquetteOrientation.DOWN, schedule_xx_down),\n", - " BaseXXPlaquette(dummy_circuit(), PlaquetteOrientation.DOWN),\n", - " BaseXXPlaquette(dummy_circuit(), PlaquetteOrientation.DOWN),\n", - " BaseXXPlaquette(dummy_circuit(), PlaquetteOrientation.DOWN),\n", - " ]\n", - " ),\n", + " [\n", + " ZRoundedInitialisationPlaquette(PlaquetteOrientation.DOWN),\n", + " XXMemoryPlaquette(\n", + " PlaquetteOrientation.DOWN,\n", + " schedule_xx_down,\n", + " include_detector=False,\n", + " is_first_round=True,\n", + " ),\n", + " XXMemoryPlaquette(PlaquetteOrientation.DOWN, schedule_xx_down),\n", + " XXMemoryPlaquette(PlaquetteOrientation.DOWN, schedule_xx_down),\n", + " XXMemoryPlaquette(PlaquetteOrientation.DOWN, schedule_xx_down),\n", + " EmptyRoundedPlaquette(PlaquetteOrientation.DOWN),\n", + " EmptyRoundedPlaquette(PlaquetteOrientation.DOWN),\n", + " EmptyRoundedPlaquette(PlaquetteOrientation.DOWN),\n", + " ],\n", " # 7\n", - " PlaquetteList(\n", - " [\n", - " ZZInitialisationPlaquette(\n", - " PlaquetteOrientation.LEFT, schedule_zz_left, include_detector=True\n", - " ),\n", - " ZZMemoryPlaquette(PlaquetteOrientation.LEFT, schedule_zz_left),\n", - " ZZMemoryPlaquette(PlaquetteOrientation.LEFT, schedule_zz_left),\n", - " ZZMemoryPlaquette(PlaquetteOrientation.LEFT, schedule_zz_left),\n", - " ZZFinalMeasurementPlaquette(PlaquetteOrientation.LEFT),\n", - " BaseZZPlaquette(dummy_circuit(), PlaquetteOrientation.LEFT),\n", - " BaseZZPlaquette(dummy_circuit(), PlaquetteOrientation.LEFT),\n", - " ]\n", - " ),\n", + " [\n", + " ZRoundedInitialisationPlaquette(PlaquetteOrientation.LEFT),\n", + " ZZMemoryPlaquette(\n", + " PlaquetteOrientation.LEFT,\n", + " schedule_zz_left,\n", + " is_first_round=True,\n", + " ),\n", + " ZZMemoryPlaquette(PlaquetteOrientation.LEFT, schedule_zz_left),\n", + " ZZMemoryPlaquette(PlaquetteOrientation.LEFT, schedule_zz_left),\n", + " ZZMemoryPlaquette(PlaquetteOrientation.LEFT, schedule_zz_left),\n", + " MeasurementRoundedPlaquette(PlaquetteOrientation.LEFT),\n", + " EmptyRoundedPlaquette(PlaquetteOrientation.LEFT),\n", + " EmptyRoundedPlaquette(PlaquetteOrientation.LEFT),\n", + " ],\n", " # 8\n", - " PlaquetteList(\n", - " [\n", - " BaseXXXXPlaquette(dummy_circuit()),\n", - " BaseXXXXPlaquette(dummy_circuit()),\n", - " XXXXInitialisationPlaquette(schedule_xxxx, include_detector=False),\n", - " XXXXMemoryPlaquette(schedule_xxxx),\n", - " BaseXXXXPlaquette(dummy_circuit()),\n", - " XXXXMemoryPlaquette(schedule_xxxx),\n", - " XXXXFinalMeasurementPlaquette(include_detector=False),\n", - " ]\n", - " ),\n", + " [\n", + " ZSquareInitialisationPlaquette(),\n", + " EmptySquarePlaquette(),\n", + " EmptySquarePlaquette(),\n", + " XXXXMemoryPlaquette(schedule_xxxx, include_detector=False, is_first_round=True),\n", + " XXXXMemoryPlaquette(schedule_xxxx),\n", + " EmptySquarePlaquette(),\n", + " XXXXMemoryPlaquette(schedule_xxxx),\n", + " MeasurementSquarePlaquette(include_detector=False),\n", + " ],\n", " # 9\n", - " PlaquetteList(\n", - " [\n", - " BaseZZZZPlaquette(dummy_circuit()),\n", - " BaseZZZZPlaquette(dummy_circuit()),\n", - " ZZZZInitialisationPlaquette(schedule_zzzz),\n", - " ZZZZMemoryPlaquette(schedule_zzzz),\n", - " BaseZZZZPlaquette(dummy_circuit()),\n", - " ZZZZMemoryPlaquette(schedule_zzzz),\n", - " ZZZZFinalMeasurementPlaquette(),\n", - " ]\n", - " ),\n", + " [\n", + " ZSquareInitialisationPlaquette(),\n", + " EmptySquarePlaquette(),\n", + " EmptySquarePlaquette(),\n", + " ZZZZMemoryPlaquette(schedule_zzzz, is_first_round=True),\n", + " ZZZZMemoryPlaquette(schedule_zzzz),\n", + " EmptySquarePlaquette(),\n", + " ZZZZMemoryPlaquette(schedule_zzzz),\n", + " MeasurementSquarePlaquette(),\n", + " ],\n", " # 10\n", - " PlaquetteList(\n", - " [\n", - " BaseXXXXPlaquette(dummy_circuit()),\n", - " BaseXXXXPlaquette(dummy_circuit()),\n", - " XXXXMemoryPlaquette(schedule_xxxx, include_detector=False),\n", - " XXXXMemoryPlaquette(schedule_xxxx),\n", - " BaseXXXXPlaquette(dummy_circuit()),\n", - " XXXXMemoryPlaquette(schedule_xxxx),\n", - " XXXXFinalMeasurementPlaquette(include_detector=False),\n", - " ]\n", - " ),\n", + " [\n", + " ZSquareInitialisationPlaquette(),\n", + " EmptySquarePlaquette(),\n", + " EmptySquarePlaquette(),\n", + " XXXXMemoryPlaquette(schedule_xxxx, include_detector=False, is_first_round=True),\n", + " XXXXMemoryPlaquette(schedule_xxxx),\n", + " EmptySquarePlaquette(),\n", + " XXXXMemoryPlaquette(schedule_xxxx),\n", + " MeasurementSquarePlaquette(include_detector=False),\n", + " ],\n", " # 11\n", - " PlaquetteList(\n", - " [\n", - " ZZInitialisationPlaquette(PlaquetteOrientation.RIGHT, schedule_zz_right),\n", - " ZZMemoryPlaquette(PlaquetteOrientation.RIGHT, schedule_zz_right),\n", - " ZZZZMemoryPlaquette(schedule_zzzz),\n", - " ZZZZMemoryPlaquette(schedule_zzzz),\n", - " BaseZZZZPlaquette(dummy_circuit()),\n", - " ZZZZMemoryPlaquette(schedule_zzzz),\n", - " ZZZZFinalMeasurementPlaquette(),\n", - " ]\n", - " ),\n", + " [\n", + " ZRoundedInitialisationPlaquette(PlaquetteOrientation.RIGHT),\n", + " ZZMemoryPlaquette(\n", + " PlaquetteOrientation.RIGHT, schedule_zz_right, is_first_round=True\n", + " ),\n", + " ZZMemoryPlaquette(PlaquetteOrientation.RIGHT, schedule_zz_right),\n", + " ZZZZMemoryPlaquette(schedule_zzzz),\n", + " ZZZZMemoryPlaquette(schedule_zzzz),\n", + " EmptySquarePlaquette(),\n", + " ZZZZMemoryPlaquette(schedule_zzzz),\n", + " MeasurementSquarePlaquette(),\n", + " ],\n", " # 12\n", - " PlaquetteList(\n", - " [\n", - " BaseXXPlaquette(dummy_circuit(), PlaquetteOrientation.UP),\n", - " BaseXXPlaquette(dummy_circuit(), PlaquetteOrientation.UP),\n", - " XXInitialisationPlaquette(\n", - " PlaquetteOrientation.UP, schedule_xx_up, include_detector=False\n", - " ),\n", - " XXMemoryPlaquette(PlaquetteOrientation.UP, schedule_xx_up),\n", - " BaseXXPlaquette(dummy_circuit(), PlaquetteOrientation.UP),\n", - " XXMemoryPlaquette(PlaquetteOrientation.UP, schedule_xx_up),\n", - " XXFinalMeasurementPlaquette(\n", - " PlaquetteOrientation.UP, include_detector=False\n", - " ),\n", - " ]\n", - " ),\n", + " [\n", + " ZRoundedInitialisationPlaquette(PlaquetteOrientation.UP),\n", + " EmptyRoundedPlaquette(PlaquetteOrientation.UP),\n", + " EmptyRoundedPlaquette(PlaquetteOrientation.UP),\n", + " XXMemoryPlaquette(\n", + " PlaquetteOrientation.UP,\n", + " schedule_xx_up,\n", + " include_detector=False,\n", + " is_first_round=True,\n", + " ),\n", + " XXMemoryPlaquette(PlaquetteOrientation.UP, schedule_xx_up),\n", + " EmptyRoundedPlaquette(PlaquetteOrientation.UP),\n", + " XXMemoryPlaquette(PlaquetteOrientation.UP, schedule_xx_up),\n", + " MeasurementRoundedPlaquette(PlaquetteOrientation.UP, include_detector=False),\n", + " ],\n", " # 13\n", - " PlaquetteList(\n", - " [\n", - " BaseXXPlaquette(dummy_circuit(), PlaquetteOrientation.DOWN),\n", - " BaseXXPlaquette(dummy_circuit(), PlaquetteOrientation.DOWN),\n", - " XXMemoryPlaquette(\n", - " PlaquetteOrientation.DOWN, schedule_xx_down, include_detector=False\n", - " ),\n", - " XXMemoryPlaquette(PlaquetteOrientation.DOWN, schedule_xx_down),\n", - " BaseXXPlaquette(dummy_circuit(), PlaquetteOrientation.DOWN),\n", - " XXMemoryPlaquette(PlaquetteOrientation.DOWN, schedule_xx_down),\n", - " XXFinalMeasurementPlaquette(\n", - " PlaquetteOrientation.DOWN, include_detector=False\n", - " ),\n", - " ]\n", - " ),\n", + " [\n", + " ZRoundedInitialisationPlaquette(PlaquetteOrientation.DOWN),\n", + " EmptyRoundedPlaquette(PlaquetteOrientation.DOWN),\n", + " EmptyRoundedPlaquette(PlaquetteOrientation.DOWN),\n", + " XXMemoryPlaquette(\n", + " PlaquetteOrientation.DOWN,\n", + " schedule_xx_down,\n", + " include_detector=False,\n", + " is_first_round=True,\n", + " ),\n", + " XXMemoryPlaquette(PlaquetteOrientation.DOWN, schedule_xx_down),\n", + " EmptyRoundedPlaquette(PlaquetteOrientation.DOWN),\n", + " XXMemoryPlaquette(PlaquetteOrientation.DOWN, schedule_xx_down),\n", + " MeasurementRoundedPlaquette(PlaquetteOrientation.DOWN, include_detector=False),\n", + " ],\n", " # 14\n", - " PlaquetteList(\n", - " [\n", - " BaseZZPlaquette(dummy_circuit(), PlaquetteOrientation.RIGHT),\n", - " BaseZZPlaquette(dummy_circuit(), PlaquetteOrientation.RIGHT),\n", - " ZZInitialisationPlaquette(\n", - " PlaquetteOrientation.RIGHT, schedule_zz_right, include_detector=True\n", - " ),\n", - " ZZMemoryPlaquette(PlaquetteOrientation.RIGHT, schedule_zz_right),\n", - " BaseZZPlaquette(dummy_circuit(), PlaquetteOrientation.RIGHT),\n", - " ZZMemoryPlaquette(PlaquetteOrientation.RIGHT, schedule_zz_right),\n", - " ZZFinalMeasurementPlaquette(PlaquetteOrientation.RIGHT),\n", - " ]\n", - " ),\n", + " [\n", + " ZRoundedInitialisationPlaquette(PlaquetteOrientation.RIGHT),\n", + " EmptyRoundedPlaquette(PlaquetteOrientation.RIGHT),\n", + " EmptyRoundedPlaquette(PlaquetteOrientation.RIGHT),\n", + " ZZMemoryPlaquette(\n", + " PlaquetteOrientation.RIGHT,\n", + " schedule_zz_right,\n", + " is_first_round=True,\n", + " ),\n", + " ZZMemoryPlaquette(PlaquetteOrientation.RIGHT, schedule_zz_right),\n", + " EmptyRoundedPlaquette(PlaquetteOrientation.RIGHT),\n", + " ZZMemoryPlaquette(PlaquetteOrientation.RIGHT, schedule_zz_right),\n", + " MeasurementRoundedPlaquette(PlaquetteOrientation.RIGHT),\n", + " ],\n", "]" ] }, @@ -504,7 +511,7 @@ " def modify_detector_then_repeat(circuit: cirq.Circuit) -> cirq.Circuit:\n", " replacements = []\n", " syndrome_qubits = [\n", - " cirq.GridQubit(row, 2 * d - 1) for row in range(3, 3 + (d - 1) * 2, 4)\n", + " cirq.GridQubit(row, 2 * d - 2) for row in range(2, 2 + (d - 1) * 2, 4)\n", " ]\n", " for moment_idx, op in circuit.findall_operations(\n", " lambda op: isinstance(op.untagged, Detector)\n", @@ -530,22 +537,22 @@ " return replaced_circuit\n", "\n", " layer_modificators = {\n", - " 1: functools.partial(make_repeated_layer, repetitions=d - 1),\n", - " 3: functools.partial(make_repeated_layer, repetitions=d - 1),\n", - " 5: modify_detector_then_repeat,\n", + " 2: functools.partial(make_repeated_layer, repetitions=d - 1),\n", + " 4: functools.partial(make_repeated_layer, repetitions=d - 1),\n", + " 6: modify_detector_then_repeat,\n", " }\n", "\n", " circuit = cirq.Circuit()\n", - " for layer_index in range(7):\n", + " for layer_index in range(8):\n", " layer_circuit = generate_circuit(\n", " template,\n", - " [plaquette_list.plaquettes[layer_index] for plaquette_list in PLAQUETTES],\n", + " [plaquette_list[layer_index] for plaquette_list in PLAQUETTES],\n", " )\n", " layer_circuit = normalise_circuit(layer_circuit)\n", " circuit += layer_modificators.get(layer_index, lambda circ: circ)(layer_circuit)\n", "\n", " # add the observable\n", - " origin = cirq.GridQubit(d + 1, 2)\n", + " origin = cirq.GridQubit(d, 1)\n", " circuit.append(\n", " cirq.Moment(\n", " make_observable(\n", diff --git a/src/tqec/__init__.py b/src/tqec/__init__.py index 5defd0b5..27b91184 100644 --- a/src/tqec/__init__.py +++ b/src/tqec/__init__.py @@ -35,7 +35,6 @@ ) from .plaquette import ( Plaquette, - PlaquetteList, PlaquetteQubit, RoundedPlaquette, ScheduledCircuit, diff --git a/src/tqec/detectors/measurement_map.py b/src/tqec/detectors/measurement_map.py index 9708e3ee..42d0a3ae 100644 --- a/src/tqec/detectors/measurement_map.py +++ b/src/tqec/detectors/measurement_map.py @@ -47,11 +47,11 @@ def get_measurement_relative_offset( Args: current_moment_index: the moment index for which we want to compute the offset. This method will only backtrack in time, and so will - never return measurements that are performed strictly after the - moment provided in this parameter. Also, the measurement record - offset is a local quantity that might change in time (due to + never return measurements that are performed strictly after the + moment provided in this parameter. Also, the measurement record + offset is a local quantity that might change in time (due to subsequent measurements shifting the offset), meaning that the - returned offset should only be considered valid for the moment + returned offset should only be considered valid for the moment provided here, and for no other moments. qubit: qubit instance the measurement we are searching for has been performed on. @@ -157,6 +157,14 @@ def _get_global_measurement_index( return global_measurement_indices, global_measurement_index + def _global_measurement_indices_as_dict(self) -> dict[int, dict[cirq.Qid, int]]: + return {i: v for i, v in enumerate(self._global_measurement_indices) if v} + + def __repr__(self) -> str: + return ( + f"{self.__class__.__name__}({self._global_measurement_indices_as_dict()})" + ) + def compute_global_measurements_lookback_offsets( relative_measurements_record: RelativeMeasurementsRecord, @@ -198,7 +206,7 @@ def compute_global_measurements_lookback_offsets( raise TQECException( "An error happened during the measurement offset lookback computation. " f"You asked for the {relative_measurement_offset} measurement on {qubit} " - f"at the moment {current_moment_index}. The computed measurement map is" + f"at the moment {current_moment_index}. The computed measurement map is " f"{measurement_map}." ) global_measurements_lookback_offsets.append(relative_offset) diff --git a/src/tqec/detectors/operation.py b/src/tqec/detectors/operation.py index 90ed3dea..0102c983 100644 --- a/src/tqec/detectors/operation.py +++ b/src/tqec/detectors/operation.py @@ -59,6 +59,9 @@ def qubits(self) -> tuple[cirq.Qid, ...]: def with_qubits(self, *new_qubits: cirq.Qid) -> "ShiftCoords": return self + def __repr__(self) -> str: + return f"{self.__class__.__name__}{self._shifts}" + @dataclass(frozen=True) class RelativeMeasurementData: @@ -92,6 +95,9 @@ def __post_init__(self): f"but got {self.relative_measurement_offset}." ) + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.relative_qubit_positioning}, {self.relative_measurement_offset})" + class RelativeMeasurementsRecord(cirq.Operation): def __init__( @@ -141,6 +147,9 @@ def origin(self, new_origin: cirq.GridQubit): """The origin of the local coordinate system.""" self._local_coordinate_system_origin = new_origin + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self._local_coordinate_system_origin}, {self._data})" + class Detector(RelativeMeasurementsRecord): def __init__( diff --git a/src/tqec/enums.py b/src/tqec/enums.py index ccf9c069..774f6cf8 100644 --- a/src/tqec/enums.py +++ b/src/tqec/enums.py @@ -43,3 +43,20 @@ class PlaquetteOrientation(Enum): LEFT = auto() DOWN = auto() UP = auto() + + def to_plaquette_side(self) -> "PlaquetteSide": + if self == PlaquetteOrientation.RIGHT: + return PlaquetteSide.LEFT + elif self == PlaquetteOrientation.LEFT: + return PlaquetteSide.RIGHT + elif self == PlaquetteOrientation.DOWN: + return PlaquetteSide.UP + else: # if self == PlaquetteOrientation.UP: + return PlaquetteSide.DOWN + + +class PlaquetteSide(Enum): + RIGHT = auto() + LEFT = auto() + DOWN = auto() + UP = auto() diff --git a/src/tqec/generation/circuit_test.py b/src/tqec/generation/circuit_test.py index 2f7dfd70..98cab211 100644 --- a/src/tqec/generation/circuit_test.py +++ b/src/tqec/generation/circuit_test.py @@ -6,12 +6,16 @@ from tqec.enums import PlaquetteOrientation from tqec.generation.circuit import generate_circuit from tqec.plaquette.library import ( - XXPlaquetteList, - XXXXPlaquetteList, - ZZPlaquetteList, - ZZZZPlaquetteList, + MeasurementRoundedPlaquette, + MeasurementSquarePlaquette, + XXMemoryPlaquette, + XXXXMemoryPlaquette, + ZRoundedInitialisationPlaquette, + ZSquareInitialisationPlaquette, + ZZMemoryPlaquette, + ZZZZMemoryPlaquette, ) -from tqec.plaquette.plaquette import PlaquetteList +from tqec.plaquette.plaquette import Plaquette from tqec.templates.constructions.qubit import QubitSquareTemplate from tqec.templates.scale import Dimension, LinearFunction @@ -41,28 +45,73 @@ def _make_repeated_layer(repeat_circuit: cirq.Circuit) -> cirq.Circuit: def _generate_circuit() -> cirq.Circuit: template = QubitSquareTemplate(Dimension(2, LinearFunction(2))) - plaquettes: list[PlaquetteList] = [ - XXPlaquetteList( - PlaquetteOrientation.UP, [1, 2, 5, 6, 7, 8], include_detector=False - ), - ZZPlaquetteList(PlaquetteOrientation.LEFT, [1, 5, 6, 8]), - XXXXPlaquetteList([1, 2, 3, 4, 5, 6, 7, 8], include_detector=False), - ZZZZPlaquetteList([1, 3, 4, 5, 6, 8]), - ZZPlaquetteList(PlaquetteOrientation.RIGHT, [1, 3, 4, 8]), - XXPlaquetteList( - PlaquetteOrientation.DOWN, [1, 2, 3, 4, 7, 8], include_detector=False - ), + plaquettes: list[list[Plaquette]] = [ + [ + ZRoundedInitialisationPlaquette(PlaquetteOrientation.UP), + XXMemoryPlaquette( + PlaquetteOrientation.UP, + [1, 2, 5, 6, 7, 8], + include_detector=False, + is_first_round=True, + ), + XXMemoryPlaquette(PlaquetteOrientation.UP, [1, 2, 5, 6, 7, 8]), + MeasurementRoundedPlaquette( + PlaquetteOrientation.UP, include_detector=False + ), + ], + [ + ZRoundedInitialisationPlaquette(PlaquetteOrientation.LEFT), + ZZMemoryPlaquette( + PlaquetteOrientation.LEFT, [1, 5, 6, 8], is_first_round=True + ), + ZZMemoryPlaquette(PlaquetteOrientation.LEFT, [1, 5, 6, 8]), + MeasurementRoundedPlaquette(PlaquetteOrientation.LEFT), + ], + [ + ZSquareInitialisationPlaquette(), + XXXXMemoryPlaquette( + [1, 2, 3, 4, 5, 6, 7, 8], include_detector=False, is_first_round=True + ), + XXXXMemoryPlaquette([1, 2, 3, 4, 5, 6, 7, 8]), + MeasurementSquarePlaquette(include_detector=False), + ], + [ + ZSquareInitialisationPlaquette(), + ZZZZMemoryPlaquette([1, 3, 4, 5, 6, 8], is_first_round=True), + ZZZZMemoryPlaquette([1, 3, 4, 5, 6, 8]), + MeasurementSquarePlaquette(), + ], + [ + ZRoundedInitialisationPlaquette(PlaquetteOrientation.RIGHT), + ZZMemoryPlaquette( + PlaquetteOrientation.RIGHT, [1, 3, 4, 8], is_first_round=True + ), + ZZMemoryPlaquette(PlaquetteOrientation.RIGHT, [1, 3, 4, 8]), + MeasurementRoundedPlaquette(PlaquetteOrientation.RIGHT), + ], + [ + ZRoundedInitialisationPlaquette(PlaquetteOrientation.DOWN), + XXMemoryPlaquette( + PlaquetteOrientation.DOWN, + [1, 2, 3, 4, 7, 8], + include_detector=False, + is_first_round=True, + ), + XXMemoryPlaquette(PlaquetteOrientation.DOWN, [1, 2, 3, 4, 7, 8]), + MeasurementRoundedPlaquette( + PlaquetteOrientation.DOWN, include_detector=False + ), + ], ] - layer_modificators = {1: _make_repeated_layer} # 4. Actually create the cirq.Circuit instance by concatenating the circuits generated # for each layers and potentially modified by the modifiers defined above. circuit = cirq.Circuit() - for layer_index in range(3): + for layer_index in range(4): layer_circuit = generate_circuit( template, - [plaquette_list.plaquettes[layer_index] for plaquette_list in plaquettes], + [plaquette_list[layer_index] for plaquette_list in plaquettes], ) layer_circuit = _normalise_circuit(layer_circuit) circuit += layer_modificators.get(layer_index, lambda circ: circ)(layer_circuit) @@ -77,55 +126,55 @@ def test_generate_circuit(): generated_circuit = _generate_circuit() generate_qubits = [(q.row, q.col) for q in generated_circuit.all_qubits()] target_qubits = [ - (7, 3), - (4, 8), - (4, 10), + (6, 2), + (3, 7), + (3, 9), + (1, 1), + (10, 6), + (8, 8), + (1, 5), + (2, 0), + (6, 4), (2, 2), - (11, 7), - (9, 9), - (2, 6), - (3, 1), + (6, 6), + (1, 3), + (7, 1), + (7, 3), + (4, 2), + (1, 7), (7, 5), - (3, 3), + (1, 9), (7, 7), (2, 4), - (8, 2), - (8, 4), + (6, 8), + (9, 1), + (2, 6), + (9, 3), + (9, 7), + (4, 6), + (4, 4), + (7, 9), + (5, 1), (5, 3), (2, 8), - (8, 6), - (2, 10), - (8, 8), - (3, 5), - (7, 9), - (10, 2), - (3, 7), - (10, 4), - (10, 8), + (9, 5), (5, 7), + (8, 2), + (0, 4), + (9, 9), + (3, 1), + (4, 8), (5, 5), - (8, 10), - (6, 2), - (6, 4), - (3, 9), - (10, 6), - (6, 8), - (9, 3), - (1, 5), - (10, 10), - (4, 2), + (3, 3), + (4, 10), + (3, 5), + (10, 2), + (8, 4), (5, 9), - (6, 6), - (4, 4), - (5, 11), - (4, 6), - (11, 3), - (9, 5), - (6, 10), - (9, 7), - (1, 9), - (7, 1), - (9, 11), + (8, 6), + (0, 8), + (6, 0), + (8, 10), ] generate_qubits.sort() target_qubits.sort() diff --git a/src/tqec/plaquette/__init__.py b/src/tqec/plaquette/__init__.py index 06c0acbd..690a48f4 100644 --- a/src/tqec/plaquette/__init__.py +++ b/src/tqec/plaquette/__init__.py @@ -1,13 +1,10 @@ from .plaquette import ( Plaquette, - SquarePlaquette, RoundedPlaquette, - PlaquetteList, + SquarePlaquette, ) - from .qubit import PlaquetteQubit - from .schedule import ( - ScheduleException, ScheduledCircuit, + ScheduleException, ) diff --git a/src/tqec/plaquette/library/__init__.py b/src/tqec/plaquette/library/__init__.py index f3cd56f2..7f303673 100644 --- a/src/tqec/plaquette/library/__init__.py +++ b/src/tqec/plaquette/library/__init__.py @@ -1,28 +1,12 @@ -from .xx import ( - BaseXXPlaquette, - XXFinalMeasurementPlaquette, - XXInitialisationPlaquette, - XXMemoryPlaquette, - XXPlaquetteList, -) -from .xxxx import ( - BaseXXXXPlaquette, - XXXXFinalMeasurementPlaquette, - XXXXInitialisationPlaquette, - XXXXMemoryPlaquette, - XXXXPlaquetteList, -) -from .zz import ( - BaseZZPlaquette, - ZZFinalMeasurementPlaquette, - ZZInitialisationPlaquette, - ZZMemoryPlaquette, - ZZPlaquetteList, -) -from .zzzz import ( - BaseZZZZPlaquette, - ZZZZFinalMeasurementPlaquette, - ZZZZInitialisationPlaquette, - ZZZZMemoryPlaquette, - ZZZZPlaquetteList, -) +from .empty import EmptyRoundedPlaquette, EmptySquarePlaquette +from .initialisation import ( + XRoundedInitialisationPlaquette, + XSquareInitialisationPlaquette, + ZRoundedInitialisationPlaquette, + ZSquareInitialisationPlaquette, +) +from .measurement import MeasurementRoundedPlaquette, MeasurementSquarePlaquette +from .xx import XXMemoryPlaquette +from .xxxx import XXXXMemoryPlaquette +from .zz import ZZMemoryPlaquette +from .zzzz import ZZZZMemoryPlaquette diff --git a/src/tqec/plaquette/library/empty.py b/src/tqec/plaquette/library/empty.py new file mode 100644 index 00000000..98ab4723 --- /dev/null +++ b/src/tqec/plaquette/library/empty.py @@ -0,0 +1,14 @@ +import cirq +from tqec.enums import PlaquetteOrientation +from tqec.plaquette.plaquette import RoundedPlaquette, SquarePlaquette +from tqec.plaquette.schedule import ScheduledCircuit + + +class EmptySquarePlaquette(SquarePlaquette): + def __init__(self) -> None: + super().__init__(ScheduledCircuit(cirq.Circuit())) + + +class EmptyRoundedPlaquette(RoundedPlaquette): + def __init__(self, orientation: PlaquetteOrientation) -> None: + super().__init__(ScheduledCircuit(cirq.Circuit()), orientation) diff --git a/src/tqec/plaquette/library/initialisation.py b/src/tqec/plaquette/library/initialisation.py new file mode 100644 index 00000000..33b4c326 --- /dev/null +++ b/src/tqec/plaquette/library/initialisation.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +import typing as ty + +import cirq +from tqec.enums import PlaquetteOrientation +from tqec.plaquette.plaquette import RoundedPlaquette, SquarePlaquette +from tqec.plaquette.schedule import ScheduledCircuit + + +class ZSquareInitialisationPlaquette(SquarePlaquette): + def __init__( + self, qubits_to_initialise: ty.Sequence[cirq.GridQubit] | None = None + ) -> None: + if qubits_to_initialise is None: + qubits_to_initialise = ( + SquarePlaquette.get_data_qubits_cirq() + + SquarePlaquette.get_syndrome_qubits_cirq() + ) + circuit = cirq.Circuit( + cirq.R(q).with_tags(self._MERGEABLE_TAG) for q in qubits_to_initialise + ) + super().__init__(ScheduledCircuit(circuit)) + + +class ZRoundedInitialisationPlaquette(RoundedPlaquette): + def __init__( + self, + orientation: PlaquetteOrientation, + qubits_to_initialise: ty.Sequence[cirq.GridQubit] | None = None, + ) -> None: + if qubits_to_initialise is None: + qubits_to_initialise = ( + RoundedPlaquette.get_data_qubits_cirq(orientation) + + RoundedPlaquette.get_syndrome_qubits_cirq() + ) + circuit = cirq.Circuit( + cirq.R(q).with_tags(self._MERGEABLE_TAG) for q in qubits_to_initialise + ) + super().__init__(ScheduledCircuit(circuit), orientation) + + +class XSquareInitialisationPlaquette(SquarePlaquette): + def __init__( + self, qubits_to_initialise: ty.Sequence[cirq.GridQubit] | None = None + ) -> None: + if qubits_to_initialise is None: + qubits_to_initialise = ( + SquarePlaquette.get_data_qubits_cirq() + + SquarePlaquette.get_syndrome_qubits_cirq() + ) + circuit = cirq.Circuit( + (cirq.R(q).with_tags(self._MERGEABLE_TAG), cirq.H(q)) + for q in qubits_to_initialise + ) + super().__init__(ScheduledCircuit(circuit)) + + +class XRoundedInitialisationPlaquette(RoundedPlaquette): + def __init__( + self, + orientation: PlaquetteOrientation, + qubits_to_initialise: ty.Sequence[cirq.GridQubit] | None = None, + ) -> None: + if qubits_to_initialise is None: + qubits_to_initialise = ( + RoundedPlaquette.get_data_qubits_cirq(orientation) + + RoundedPlaquette.get_syndrome_qubits_cirq() + ) + circuit = cirq.Circuit( + (cirq.R(q).with_tags(self._MERGEABLE_TAG), cirq.H(q)) + for q in qubits_to_initialise + ) + super().__init__(ScheduledCircuit(circuit), orientation) diff --git a/src/tqec/plaquette/library/measurement.py b/src/tqec/plaquette/library/measurement.py new file mode 100644 index 00000000..ebbbd112 --- /dev/null +++ b/src/tqec/plaquette/library/measurement.py @@ -0,0 +1,61 @@ +import cirq +from tqec.detectors.operation import make_detector +from tqec.enums import PlaquetteOrientation +from tqec.plaquette.plaquette import RoundedPlaquette, SquarePlaquette +from tqec.plaquette.schedule import ScheduledCircuit + + +class MeasurementRoundedPlaquette(RoundedPlaquette): + def __init__( + self, + orientation: PlaquetteOrientation, + include_detector: bool = True, + ): + (syndrome_qubit,) = self.get_syndrome_qubits_cirq() + data_qubits = self.get_data_qubits_cirq(orientation) + measurement_qubits = [syndrome_qubit, *data_qubits] + detector = make_detector( + syndrome_qubit, + [(meas_qubit - syndrome_qubit, -1) for meas_qubit in measurement_qubits], + ) + super().__init__( + circuit=ScheduledCircuit( + cirq.Circuit( + [ + cirq.Moment( + cirq.M(q).with_tags(self._MERGEABLE_TAG) + for q in data_qubits + ), + cirq.Moment(detector) if include_detector else [], + ] + ), + ), + orientation=orientation, + ) + + +class MeasurementSquarePlaquette(SquarePlaquette): + def __init__( + self, + include_detector: bool = True, + ): + (syndrome_qubit,) = self.get_syndrome_qubits_cirq() + data_qubits = self.get_data_qubits_cirq() + measurement_qubits = [syndrome_qubit, *data_qubits] + detector = make_detector( + syndrome_qubit, + [(meas_qubit - syndrome_qubit, -1) for meas_qubit in measurement_qubits], + ) + super().__init__( + circuit=ScheduledCircuit( + cirq.Circuit( + [ + cirq.Moment( + cirq.M(q).with_tags(self._MERGEABLE_TAG) + for q in data_qubits + ), + cirq.Moment(detector) if include_detector else [], + ] + ), + ), + ) diff --git a/src/tqec/plaquette/library/utils/detectors.py b/src/tqec/plaquette/library/utils/detectors.py new file mode 100644 index 00000000..8c25c54b --- /dev/null +++ b/src/tqec/plaquette/library/utils/detectors.py @@ -0,0 +1,26 @@ +import cirq +from tqec.detectors.operation import RelativeMeasurementData, make_detector +from tqec.plaquette.qubit import PlaquetteQubit + + +def make_memory_experiment_detector( + syndrome_qubit: PlaquetteQubit, + is_first_round: bool = False, +) -> cirq.Operation: + """Build the Detector operation for a memory experiment + + Args: + syndrome_qubit: the qubit used to non-destructively measure the syndrome. + is_first_round: True if the detector is inserted on the first round of + the memory experiment, i.e., if only one measurement already + happened on the provided ``syndrome_qubit``. + + Returns: + an instance of cirq.Operation representing a detector. + """ + detector_relative_measurements = [RelativeMeasurementData(cirq.GridQubit(0, 0), -1)] + if not is_first_round: + detector_relative_measurements.append( + RelativeMeasurementData(cirq.GridQubit(0, 0), -2) + ) + return make_detector(syndrome_qubit.to_grid_qubit(), detector_relative_measurements) diff --git a/src/tqec/plaquette/library/utils/pauli.py b/src/tqec/plaquette/library/utils/pauli.py new file mode 100644 index 00000000..4abe3b7e --- /dev/null +++ b/src/tqec/plaquette/library/utils/pauli.py @@ -0,0 +1,77 @@ +from __future__ import annotations + +import cirq +from tqec.exceptions import TQECException +from tqec.plaquette.qubit import PlaquetteQubit + +_SUPPORTED_PAULI: set[str] = set("xz") + + +def make_pauli_syndrome_measurement_circuit( + syndrome_qubit: PlaquetteQubit, + data_qubits: list[PlaquetteQubit], + pauli_string: str, + reset_syndrome_qubit: bool = True, +) -> cirq.Circuit: + """Build and return a quantum circuit measuring the provided Pauli syndrome. + + This function builds a quantum circuit measuring the Pauli observable + provided in ``pauli_string`` on the provided ``data_qubits``, using + ``syndrome_qubit`` as an ancilla. + + Args: + syndrome_qubit: the ``PlaquetteQubit`` instance used to measure the + syndrome. Will be reset at the beginning of the returned circuit + if ``reset_syndrome_qubit`` is ``True``. + data_qubits: the qubits we should measure the Pauli observable on. + There should be as many ``PlaquetteQubit`` instances in + ``data_qubits`` as there are Pauli characters in the provided + ``pauli_string``. + pauli_string: a string of case-independent characters, each + representing a Pauli matrix. Each charater should be present in + _SUPPORTED_PAULI and the string should have as many characters as + there are qubits in ``data_qubits``. + reset_syndrome_qubit: insert a reset gate on the syndrome qubit at the + beginning of the circuit if True. + + Returns: + a cirq.Circuit instance measuring the provided Pauli string on the + provided syndrome_qubit. + + Raises: + TQECException: if ``len(pauli_string) != len(data_qubits)`` or + if ``any(p not in _SUPPORTED_PAULI for p in pauli_string)``. + """ + if len(pauli_string) != len(data_qubits): + raise TQECException( + f"The number of Pauli characters provided ({len(pauli_string)}) " + f"does not correspond to the number of data qubits ({len(data_qubits)})." + ) + + sq = syndrome_qubit.to_grid_qubit() + dqs = [dq.to_grid_qubit() for dq in data_qubits] + + circuit = cirq.Circuit() + if reset_syndrome_qubit: + circuit.append(cirq.Moment(cirq.R(sq))) + + is_in_X_basis: bool = False + for i, pauli in enumerate(pauli_string.lower()): + if pauli not in _SUPPORTED_PAULI: + raise TQECException(f"Unsupported Pauli operation: {pauli}.") + if pauli == "z": + if is_in_X_basis: + circuit.append(cirq.Moment(cirq.H(sq))) + is_in_X_basis = False + circuit.append(cirq.Moment(cirq.CX(dqs[i], sq))) + if pauli == "x": + if not is_in_X_basis: + circuit.append(cirq.Moment(cirq.H(sq))) + is_in_X_basis = True + circuit.append(cirq.Moment(cirq.CX(sq, dqs[i]))) + + if is_in_X_basis: + circuit.append(cirq.Moment(cirq.H(sq))) + + circuit.append(cirq.Moment(cirq.M(sq))) + return circuit diff --git a/src/tqec/plaquette/library/xx.py b/src/tqec/plaquette/library/xx.py index 6740f2ce..0d7aa466 100644 --- a/src/tqec/plaquette/library/xx.py +++ b/src/tqec/plaquette/library/xx.py @@ -1,180 +1,36 @@ +from __future__ import annotations + import cirq -from tqec.detectors.operation import make_detector + from tqec.enums import PlaquetteOrientation -from tqec.plaquette.plaquette import PlaquetteList, RoundedPlaquette +from tqec.plaquette.library.utils.detectors import make_memory_experiment_detector +from tqec.plaquette.library.utils.pauli import make_pauli_syndrome_measurement_circuit +from tqec.plaquette.plaquette import RoundedPlaquette from tqec.plaquette.schedule import ScheduledCircuit -from tqec.position import Shape2D -class BaseXXPlaquette(RoundedPlaquette): - def __init__( - self, circuit: ScheduledCircuit, orientation: PlaquetteOrientation - ) -> None: - super().__init__(circuit, orientation) - - @property - def shape(self) -> Shape2D: - # Hack to check the pre-condition that all Plaquette instances should - # have the same shape. - return Shape2D(3, 3) - - -class XXInitialisationPlaquette(BaseXXPlaquette): +class XXMemoryPlaquette(RoundedPlaquette): def __init__( self, orientation: PlaquetteOrientation, schedule: list[int], include_detector: bool = True, + is_first_round: bool = False, ): - (syndrome_qubit,) = [ - q.to_grid_qubit() for q in RoundedPlaquette.get_syndrome_qubits() - ] - data_qubits = [ - q.to_grid_qubit() for q in RoundedPlaquette.get_data_qubits(orientation) - ] - detector = ( - [ - cirq.Moment( - make_detector( - syndrome_qubit, - [(cirq.GridQubit(0, 0), -1)], - time_coordinate=0, - ) - ), - ] - if include_detector - else [] - ) - super().__init__( - circuit=ScheduledCircuit( - cirq.Circuit( - [ - cirq.Moment( - cirq.R(q).with_tags(self._MERGEABLE_TAG) - for q in [syndrome_qubit, *data_qubits] - ), - cirq.Moment(cirq.H(syndrome_qubit)), - cirq.Moment(cirq.CX(syndrome_qubit, data_qubits[0])), - cirq.Moment(cirq.CX(syndrome_qubit, data_qubits[1])), - cirq.Moment(cirq.H(syndrome_qubit)), - cirq.Moment([cirq.M(syndrome_qubit)]), - ] - + detector - ), - schedule, - ), - orientation=orientation, - ) - + (syndrome_qubit,) = RoundedPlaquette.get_syndrome_qubits() + data_qubits = RoundedPlaquette.get_data_qubits(orientation) -class XXMemoryPlaquette(BaseXXPlaquette): - def __init__( - self, - orientation: PlaquetteOrientation, - schedule: list[int], - include_detector=True, - ): - (syndrome_qubit,) = [ - q.to_grid_qubit() for q in RoundedPlaquette.get_syndrome_qubits() - ] - data_qubits = [ - q.to_grid_qubit() for q in RoundedPlaquette.get_data_qubits(orientation) - ] - detector = ( - [ - cirq.Moment( - make_detector( - syndrome_qubit, - [ - (cirq.GridQubit(0, 0), -1), - (cirq.GridQubit(0, 0), -2), - ], - time_coordinate=0, - ) - ), - ] - if include_detector - else [] - ) - super().__init__( - circuit=ScheduledCircuit( - cirq.Circuit( - [ - cirq.Moment( - cirq.R(q).with_tags(self._MERGEABLE_TAG) - for q in [syndrome_qubit] - ), - cirq.Moment(cirq.H(syndrome_qubit)), - cirq.Moment(cirq.CX(syndrome_qubit, data_qubits[0])), - cirq.Moment(cirq.CX(syndrome_qubit, data_qubits[1])), - cirq.Moment(cirq.H(syndrome_qubit)), - cirq.Moment(cirq.M(syndrome_qubit)), - ] - + detector - ), - schedule, - ), - orientation=orientation, + circuit = make_pauli_syndrome_measurement_circuit( + syndrome_qubit, data_qubits, "XX" ) - - -class XXFinalMeasurementPlaquette(BaseXXPlaquette): - def __init__( - self, - orientation: PlaquetteOrientation, - include_detector: bool = True, - ): - (syndrome_qubit,) = [ - q.to_grid_qubit() for q in RoundedPlaquette.get_syndrome_qubits() - ] - data_qubits = [ - q.to_grid_qubit() for q in RoundedPlaquette.get_data_qubits(orientation) - ] - detector = ( - [ + if include_detector: + circuit.append( cirq.Moment( - make_detector( - syndrome_qubit, - [ - (cirq.GridQubit(0, 0), -1), - *[(dq - syndrome_qubit, -1) for dq in data_qubits], - ], - time_coordinate=1, - ) + make_memory_experiment_detector(syndrome_qubit, is_first_round) ) - ] - if include_detector - else [] - ) - super().__init__( - circuit=ScheduledCircuit( - cirq.Circuit( - [ - cirq.Moment( - [ - cirq.M(q).with_tags(self._MERGEABLE_TAG) - for q in data_qubits - ] - ), - ] - + detector - ), - ), - orientation=orientation, - ) - + ) -class XXPlaquetteList(PlaquetteList): - def __init__( - self, - orientation: PlaquetteOrientation, - schedule: list[int], - include_detector: bool = True, - ): super().__init__( - [ - XXInitialisationPlaquette(orientation, schedule, include_detector), - XXMemoryPlaquette(orientation, schedule), - XXFinalMeasurementPlaquette(orientation, include_detector), - ] + ScheduledCircuit(circuit, schedule), + orientation, ) diff --git a/src/tqec/plaquette/library/xxxx.py b/src/tqec/plaquette/library/xxxx.py index 94c4e651..614dc3e6 100644 --- a/src/tqec/plaquette/library/xxxx.py +++ b/src/tqec/plaquette/library/xxxx.py @@ -1,146 +1,31 @@ +from __future__ import annotations + import cirq -from tqec.detectors.operation import make_detector -from tqec.plaquette.plaquette import PlaquetteList, SquarePlaquette +from tqec.plaquette.library.utils.detectors import make_memory_experiment_detector +from tqec.plaquette.library.utils.pauli import make_pauli_syndrome_measurement_circuit +from tqec.plaquette.plaquette import SquarePlaquette from tqec.plaquette.schedule import ScheduledCircuit -from tqec.position import Shape2D - - -class BaseXXXXPlaquette(SquarePlaquette): - def __init__(self, circuit: ScheduledCircuit) -> None: - super().__init__(circuit) - - @property - def shape(self) -> Shape2D: - return Shape2D(3, 3) -class XXXXInitialisationPlaquette(BaseXXXXPlaquette): +class XXXXMemoryPlaquette(SquarePlaquette): def __init__( self, schedule: list[int], include_detector: bool = True, + is_first_round: bool = False, ): - (syndrome_qubit,) = BaseXXXXPlaquette.get_syndrome_qubits_cirq() - data_qubits = BaseXXXXPlaquette.get_data_qubits_cirq() - detector = [ - cirq.Moment(make_detector( - syndrome_qubit, - [(cirq.GridQubit(0, 0), -1)], - time_coordinate=0, - )), - ] if include_detector else [] - super().__init__( - circuit=ScheduledCircuit( - cirq.Circuit( - [ - cirq.Moment( - cirq.R(q).with_tags(self._MERGEABLE_TAG) - for q in [syndrome_qubit, *data_qubits] - ), - cirq.Moment(cirq.H(syndrome_qubit)), - cirq.Moment(cirq.CX(syndrome_qubit, data_qubits[0])), - cirq.Moment(cirq.CX(syndrome_qubit, data_qubits[1])), - cirq.Moment(cirq.CX(syndrome_qubit, data_qubits[2])), - cirq.Moment(cirq.CX(syndrome_qubit, data_qubits[3])), - cirq.Moment(cirq.H(syndrome_qubit)), - cirq.Moment(cirq.M(syndrome_qubit)), - ] + detector - ), - schedule, - ), - ) - + (syndrome_qubit,) = SquarePlaquette.get_syndrome_qubits() + data_qubits = SquarePlaquette.get_data_qubits() -class XXXXMemoryPlaquette(BaseXXXXPlaquette): - def __init__( - self, - schedule: list[int], - include_detector: bool = True, - ): - (syndrome_qubit,) = BaseXXXXPlaquette.get_syndrome_qubits_cirq() - data_qubits = BaseXXXXPlaquette.get_data_qubits_cirq() - detector = [ - cirq.Moment(make_detector( - syndrome_qubit, - [ - (cirq.GridQubit(0, 0), -1), - (cirq.GridQubit(0, 0), -2), - ], - time_coordinate=0, - )), - ] if include_detector else [] - super().__init__( - circuit=ScheduledCircuit( - cirq.Circuit( - [ - cirq.Moment( - cirq.R(q).with_tags(self._MERGEABLE_TAG) - for q in [syndrome_qubit] - ), - cirq.Moment(cirq.H(syndrome_qubit)), - cirq.Moment(cirq.CX(syndrome_qubit, data_qubits[0])), - cirq.Moment(cirq.CX(syndrome_qubit, data_qubits[1])), - cirq.Moment(cirq.CX(syndrome_qubit, data_qubits[2])), - cirq.Moment(cirq.CX(syndrome_qubit, data_qubits[3])), - cirq.Moment(cirq.H(syndrome_qubit)), - cirq.Moment(cirq.M(syndrome_qubit)), - ] + detector - ), - schedule, - ), + circuit = make_pauli_syndrome_measurement_circuit( + syndrome_qubit, data_qubits, "XXXX" ) - - -class XXXXFinalMeasurementPlaquette(BaseXXXXPlaquette): - def __init__( - self, - include_detector: bool = True, - ): - (syndrome_qubit,) = BaseXXXXPlaquette.get_syndrome_qubits_cirq() - data_qubits = BaseXXXXPlaquette.get_data_qubits_cirq() - detector = [ - cirq.Moment( - make_detector( - syndrome_qubit, - [ - (cirq.GridQubit(0, 0), -1), - *[ - (dq - syndrome_qubit, -1) - for dq in data_qubits - ], - ], - time_coordinate=1, - ), + if include_detector: + circuit.append( + cirq.Moment( + make_memory_experiment_detector(syndrome_qubit, is_first_round) + ) ) - ] if include_detector else [] - super().__init__( - circuit=ScheduledCircuit( - cirq.Circuit( - [ - cirq.Moment( - [ - cirq.M(q).with_tags(self._MERGEABLE_TAG) - for q in data_qubits - ] - ), - ] - + detector - ), - ), - ) - -class XXXXPlaquetteList(PlaquetteList): - def __init__( - self, - schedule: list[int], - include_detector: bool = True, - ): - super().__init__( - [ - XXXXInitialisationPlaquette(schedule, include_detector), - XXXXMemoryPlaquette(schedule), - XXXXFinalMeasurementPlaquette(include_detector), - ] - ) + super().__init__(ScheduledCircuit(circuit, schedule)) diff --git a/src/tqec/plaquette/library/zz.py b/src/tqec/plaquette/library/zz.py index 6d9cca41..ec2fd8de 100644 --- a/src/tqec/plaquette/library/zz.py +++ b/src/tqec/plaquette/library/zz.py @@ -1,162 +1,36 @@ +from __future__ import annotations + import cirq -from tqec.detectors.operation import make_detector from tqec.enums import PlaquetteOrientation -from tqec.plaquette.plaquette import PlaquetteList, RoundedPlaquette +from tqec.plaquette.library.utils.detectors import make_memory_experiment_detector +from tqec.plaquette.library.utils.pauli import make_pauli_syndrome_measurement_circuit +from tqec.plaquette.plaquette import RoundedPlaquette from tqec.plaquette.schedule import ScheduledCircuit -from tqec.position import Shape2D - - -class BaseZZPlaquette(RoundedPlaquette): - def __init__( - self, circuit: ScheduledCircuit, orientation: PlaquetteOrientation - ) -> None: - super().__init__(circuit, orientation) - @property - def shape(self) -> Shape2D: - # Hack to check the pre-condition that all Plaquette instances should - # have the same shape. - return Shape2D(3, 3) - -class ZZInitialisationPlaquette(BaseZZPlaquette): +class ZZMemoryPlaquette(RoundedPlaquette): def __init__( self, orientation: PlaquetteOrientation, schedule: list[int], include_detector: bool = True, + is_first_round: bool = False, ): - (syndrome_qubit,) = [ - q.to_grid_qubit() for q in RoundedPlaquette.get_syndrome_qubits() - ] - data_qubits = [ - q.to_grid_qubit() for q in RoundedPlaquette.get_data_qubits(orientation) - ] - detector = [ - cirq.Moment(make_detector( - syndrome_qubit, - [(cirq.GridQubit(0, 0), -1)], - time_coordinate=0, - )), - ] if include_detector else [] - super().__init__( - circuit=ScheduledCircuit( - cirq.Circuit( - [ - cirq.Moment( - cirq.R(q).with_tags(self._MERGEABLE_TAG) - for q in [syndrome_qubit, *data_qubits] - ), - cirq.Moment(cirq.CX(data_qubits[0], syndrome_qubit)), - cirq.Moment(cirq.CX(data_qubits[1], syndrome_qubit)), - cirq.Moment(cirq.M(syndrome_qubit)), - ] + detector - ), - schedule, - ), - orientation=orientation, - ) - + (syndrome_qubit,) = RoundedPlaquette.get_syndrome_qubits() + data_qubits = RoundedPlaquette.get_data_qubits(orientation) -class ZZMemoryPlaquette(BaseZZPlaquette): - def __init__( - self, - orientation: PlaquetteOrientation, - schedule: list[int], - include_detector: bool = True, - ): - (syndrome_qubit,) = [ - q.to_grid_qubit() for q in RoundedPlaquette.get_syndrome_qubits() - ] - data_qubits = [ - q.to_grid_qubit() for q in RoundedPlaquette.get_data_qubits(orientation) - ] - detector = [ - cirq.Moment(make_detector( - syndrome_qubit, - [ - (cirq.GridQubit(0, 0), -1), - (cirq.GridQubit(0, 0), -2), - ], - time_coordinate=0, - )), - ] if include_detector else [] - super().__init__( - circuit=ScheduledCircuit( - cirq.Circuit( - [ - cirq.Moment( - cirq.R(q).with_tags(self._MERGEABLE_TAG) - for q in [syndrome_qubit] - ), - cirq.Moment(cirq.CX(data_qubits[0], syndrome_qubit)), - cirq.Moment(cirq.CX(data_qubits[1], syndrome_qubit)), - cirq.Moment(cirq.M(syndrome_qubit)), - ] + detector - ), - schedule, - ), - orientation=orientation, + circuit = make_pauli_syndrome_measurement_circuit( + syndrome_qubit, data_qubits, "ZZ" ) - - -class ZZFinalMeasurementPlaquette(BaseZZPlaquette): - def __init__( - self, - orientation: PlaquetteOrientation, - include_detector: bool = True, - ): - (syndrome_qubit,) = [ - q.to_grid_qubit() for q in RoundedPlaquette.get_syndrome_qubits() - ] - data_qubits = [ - q.to_grid_qubit() for q in RoundedPlaquette.get_data_qubits(orientation) - ] - detector = [ - cirq.Moment( - make_detector( - syndrome_qubit, - [ - (cirq.GridQubit(0, 0), -1), - *[ - (dq - syndrome_qubit, -1) - for dq in data_qubits - ], - ], - time_coordinate=1, - ), + if include_detector: + circuit.append( + cirq.Moment( + make_memory_experiment_detector(syndrome_qubit, is_first_round) + ) ) - ] if include_detector else [] - super().__init__( - circuit=ScheduledCircuit( - cirq.Circuit( - [ - cirq.Moment( - [ - cirq.M(q).with_tags(self._MERGEABLE_TAG) - for q in data_qubits - ] - ), - ] - + detector - ), - ), - orientation=orientation, - ) - -class ZZPlaquetteList(PlaquetteList): - def __init__( - self, - orientation: PlaquetteOrientation, - schedule: list[int], - include_detector: bool = True, - ): super().__init__( - [ - ZZInitialisationPlaquette(orientation, schedule, include_detector), - ZZMemoryPlaquette(orientation, schedule), - ZZFinalMeasurementPlaquette(orientation, include_detector), - ] + ScheduledCircuit(circuit, schedule), + orientation, ) diff --git a/src/tqec/plaquette/library/zzzz.py b/src/tqec/plaquette/library/zzzz.py index 5f06501b..b2a914d4 100644 --- a/src/tqec/plaquette/library/zzzz.py +++ b/src/tqec/plaquette/library/zzzz.py @@ -1,143 +1,31 @@ +from __future__ import annotations + import cirq -from tqec.detectors.operation import make_detector -from tqec.plaquette.plaquette import PlaquetteList, SquarePlaquette +from tqec.plaquette.library.utils.detectors import make_memory_experiment_detector +from tqec.plaquette.library.utils.pauli import make_pauli_syndrome_measurement_circuit +from tqec.plaquette.plaquette import SquarePlaquette from tqec.plaquette.schedule import ScheduledCircuit -from tqec.position import Shape2D - - -class BaseZZZZPlaquette(SquarePlaquette): - def __init__(self, circuit: ScheduledCircuit) -> None: - super().__init__( - circuit, - ) - @property - def shape(self) -> Shape2D: - return Shape2D(3, 3) - -class ZZZZInitialisationPlaquette(BaseZZZZPlaquette): +class ZZZZMemoryPlaquette(SquarePlaquette): def __init__( self, schedule: list[int], include_detector: bool = True, + is_first_round: bool = False, ): - (syndrome_qubit,) = BaseZZZZPlaquette.get_syndrome_qubits_cirq() - data_qubits = BaseZZZZPlaquette.get_data_qubits_cirq() - detector = [ - cirq.Moment(make_detector( - syndrome_qubit, - [(cirq.GridQubit(0, 0), -1)], - time_coordinate=0, - )), - ] if include_detector else [] - super().__init__( - circuit=ScheduledCircuit( - cirq.Circuit( - [ - cirq.Moment( - cirq.R(q).with_tags(self._MERGEABLE_TAG) - for q in [syndrome_qubit, *data_qubits] - ), - cirq.Moment(cirq.CX(data_qubits[0], syndrome_qubit)), - cirq.Moment(cirq.CX(data_qubits[2], syndrome_qubit)), - cirq.Moment(cirq.CX(data_qubits[1], syndrome_qubit)), - cirq.Moment(cirq.CX(data_qubits[3], syndrome_qubit)), - cirq.Moment(cirq.M(syndrome_qubit)), - ] + detector - ), - schedule, - ), - ) - + (syndrome_qubit,) = SquarePlaquette.get_syndrome_qubits() + data_qubits = SquarePlaquette.get_data_qubits() -class ZZZZMemoryPlaquette(BaseZZZZPlaquette): - def __init__( - self, - schedule: list[int], - include_detector: bool = True, - ): - (syndrome_qubit,) = BaseZZZZPlaquette.get_syndrome_qubits_cirq() - data_qubits = BaseZZZZPlaquette.get_data_qubits_cirq() - detector = [ - cirq.Moment(make_detector( - syndrome_qubit, - [ - (cirq.GridQubit(0, 0), -1), - (cirq.GridQubit(0, 0), -2), - ], - time_coordinate=0, - )), - ] if include_detector else [] - super().__init__( - circuit=ScheduledCircuit( - cirq.Circuit( - [ - cirq.Moment( - cirq.R(q).with_tags(self._MERGEABLE_TAG) - for q in [syndrome_qubit] - ), - cirq.Moment(cirq.CX(data_qubits[0], syndrome_qubit)), - cirq.Moment(cirq.CX(data_qubits[2], syndrome_qubit)), - cirq.Moment(cirq.CX(data_qubits[1], syndrome_qubit)), - cirq.Moment(cirq.CX(data_qubits[3], syndrome_qubit)), - cirq.Moment(cirq.M(syndrome_qubit)), - ] + detector - ), - schedule, - ), + circuit = make_pauli_syndrome_measurement_circuit( + syndrome_qubit, [data_qubits[i] for i in [0, 2, 1, 3]], "ZZZZ" ) - - -class ZZZZFinalMeasurementPlaquette(BaseZZZZPlaquette): - def __init__( - self, - include_detector: bool = True, - ): - (syndrome_qubit,) = BaseZZZZPlaquette.get_syndrome_qubits_cirq() - data_qubits = BaseZZZZPlaquette.get_data_qubits_cirq() - detector = [ - cirq.Moment( - make_detector( - syndrome_qubit, - [ - (cirq.GridQubit(0, 0), -1), - *[ - (dq - syndrome_qubit, -1) - for dq in data_qubits - ], - ], - time_coordinate=1, - ), + if include_detector: + circuit.append( + cirq.Moment( + make_memory_experiment_detector(syndrome_qubit, is_first_round) + ) ) - ] if include_detector else [] - super().__init__( - circuit=ScheduledCircuit( - cirq.Circuit( - [ - cirq.Moment( - [ - cirq.M(q).with_tags(self._MERGEABLE_TAG) - for q in data_qubits - ] - ), - ] + detector - ), - ), - ) - -class ZZZZPlaquetteList(PlaquetteList): - def __init__( - self, - schedule: list[int], - include_detector: bool = True, - ): - super().__init__( - [ - ZZZZInitialisationPlaquette(schedule, include_detector), - ZZZZMemoryPlaquette(schedule), - ZZZZFinalMeasurementPlaquette(include_detector), - ] - ) + super().__init__(ScheduledCircuit(circuit, schedule)) diff --git a/src/tqec/plaquette/plaquette.py b/src/tqec/plaquette/plaquette.py index 0591da74..a477119c 100644 --- a/src/tqec/plaquette/plaquette.py +++ b/src/tqec/plaquette/plaquette.py @@ -1,5 +1,6 @@ import cirq -from tqec.enums import PlaquetteOrientation + +from tqec.enums import PlaquetteOrientation, PlaquetteSide from tqec.exceptions import TQECException from tqec.plaquette.qubit import PlaquetteQubit from tqec.plaquette.schedule import ScheduledCircuit @@ -82,15 +83,18 @@ def __init__(self, circuit: ScheduledCircuit) -> None: circuit: scheduled quantum circuit implementing the computation that the plaquette should represent. """ - super().__init__(self.get_data_qubits() + self.get_syndrome_qubits(), circuit) + super().__init__( + SquarePlaquette.get_data_qubits() + SquarePlaquette.get_syndrome_qubits(), + circuit, + ) _data_qubits: list[PlaquetteQubit] = [ - PlaquetteQubit(Position(0, 0)), - PlaquetteQubit(Position(2, 0)), - PlaquetteQubit(Position(0, 2)), - PlaquetteQubit(Position(2, 2)), + PlaquetteQubit(Position(-1, -1)), + PlaquetteQubit(Position(1, -1)), + PlaquetteQubit(Position(-1, 1)), + PlaquetteQubit(Position(1, 1)), ] - _syndrome_qubits = [PlaquetteQubit(Position(1, 1))] + _syndrome_qubits = [PlaquetteQubit(Position(0, 0))] @staticmethod def get_data_qubits() -> list[PlaquetteQubit]: @@ -108,8 +112,21 @@ def get_data_qubits_cirq() -> list[cirq.GridQubit]: def get_syndrome_qubits_cirq() -> list[cirq.GridQubit]: return [q.to_grid_qubit() for q in SquarePlaquette.get_syndrome_qubits()] + @staticmethod + def get_qubits_on_side(side: PlaquetteSide) -> list[PlaquetteQubit]: + data_indices: tuple[int, int] + if side == PlaquetteSide.LEFT: + data_indices = (0, 2) + elif side == PlaquetteSide.RIGHT: + data_indices = (1, 3) + elif side == PlaquetteSide.UP: + data_indices = (0, 1) + else: # if orientation == PlaquetteSide.DOWN: + data_indices = (2, 3) + return [SquarePlaquette._data_qubits[i] for i in data_indices] + -class RoundedPlaquette(Plaquette): +class RoundedPlaquette(SquarePlaquette): def __init__( self, circuit: ScheduledCircuit, orientation: PlaquetteOrientation ) -> None: @@ -128,7 +145,7 @@ def __init__( PlaquetteOrientation.UP orientation will have its two qubits ordered as ```text ------- - / \ + / \\ | 1 2 | |---------| ``` @@ -148,19 +165,8 @@ def __init__( orientation of PlaquetteOrientation.UP will generate a plaquette with its rounded side pointing up. """ - super().__init__( - RoundedPlaquette.get_data_qubits(orientation) - + RoundedPlaquette.get_syndrome_qubits(), - circuit, - ) - - _data_qubits: list[PlaquetteQubit] = [ - PlaquetteQubit(Position(0, 0)), - PlaquetteQubit(Position(2, 0)), - PlaquetteQubit(Position(0, 2)), - PlaquetteQubit(Position(2, 2)), - ] - _syndrome_qubits = [PlaquetteQubit(Position(1, 1))] + super().__init__(circuit) + self._orientation = orientation @staticmethod def get_data_qubits(orientation: PlaquetteOrientation) -> list[PlaquetteQubit]: @@ -173,28 +179,10 @@ def get_data_qubits(orientation: PlaquetteOrientation) -> list[PlaquetteQubit]: Args: orientation: plaquette orientation """ - data_indices: tuple[int, int] - if orientation == PlaquetteOrientation.RIGHT: - data_indices = (0, 2) - elif orientation == PlaquetteOrientation.LEFT: - data_indices = (1, 3) - elif orientation == PlaquetteOrientation.DOWN: - data_indices = (0, 1) - else: # if orientation == PlaquetteOrientation.UP: - data_indices = (2, 3) - return [RoundedPlaquette._data_qubits[i] for i in data_indices] + return RoundedPlaquette.get_qubits_on_side(orientation.to_plaquette_side()) @staticmethod - def get_syndrome_qubits() -> list[PlaquetteQubit]: - return RoundedPlaquette._syndrome_qubits - - -class PlaquetteList: - """Basic wrapper over a list of Plaquette instances.""" - - def __init__(self, plaquettes: list[Plaquette]) -> None: - self._plaquettes = plaquettes - - @property - def plaquettes(self) -> list[Plaquette]: - return self._plaquettes + def get_data_qubits_cirq(orientation: PlaquetteOrientation) -> list[cirq.GridQubit]: + return [ + q.to_grid_qubit() for q in RoundedPlaquette.get_data_qubits(orientation) + ] diff --git a/src/tqec/templates/atomic/rectangle.py b/src/tqec/templates/atomic/rectangle.py index a78db9bb..299c24bc 100644 --- a/src/tqec/templates/atomic/rectangle.py +++ b/src/tqec/templates/atomic/rectangle.py @@ -60,7 +60,7 @@ def __init__( self._height = height def instantiate(self, plaquette_indices: ty.Sequence[int]) -> numpy.ndarray: - self._check_plaquette_number(plaquette_indices) + self._check_plaquette_number(plaquette_indices, 2) p1, p2 = plaquette_indices[:2] ret = numpy.zeros(self.shape.to_numpy_shape(), dtype=int) odd = slice(0, None, 2) @@ -91,6 +91,7 @@ def expected_plaquettes_number(self) -> int: return 2 +@ty.final class RawRectangleTemplate(Template): def __init__( self, @@ -108,6 +109,10 @@ def __init__( is used to compute the expected number of plaquettes to instantiate the template, that is ``1 + max(max(line) for line in indices)``.` + This template cannot be inherited from for the moment. This is to avoid + potential mistakes when sub-classing this class and overriding some of its + methods. + Args: indices: 2-dimensional list of indices that will be used to index the plaquette_indices provided to the ``instantiate`` method. Should contain @@ -166,7 +171,9 @@ def __init__( self._indices = indices def instantiate(self, plaquette_indices: ty.Sequence[int]) -> numpy.ndarray: - self._check_plaquette_number(plaquette_indices) + # Warning: self.expected_plaquettes_number is only guaranteed to be correct + # here because RawRectangleTemplate is annotated as final. + self._check_plaquette_number(plaquette_indices, self.expected_plaquettes_number) try: # Use numpy indexing to instantiate the raw values. plaquette_indices_array = numpy.array(plaquette_indices, dtype=int) diff --git a/src/tqec/templates/atomic/square.py b/src/tqec/templates/atomic/square.py index 05fdd9bd..7d10bbf6 100644 --- a/src/tqec/templates/atomic/square.py +++ b/src/tqec/templates/atomic/square.py @@ -137,7 +137,7 @@ def __init__( self._corner_position = corner_position def instantiate(self, plaquette_indices: ty.Sequence[int]) -> numpy.ndarray: - self._check_plaquette_number(plaquette_indices) + self._check_plaquette_number(plaquette_indices, 5) p1, p2, p1_flipped, p2_flipped, corner_plaquette = plaquette_indices[:5] ret = numpy.zeros(self.shape.to_numpy_shape(), dtype=int) # Fill ret as if it was in the upper-left corner and then correct diff --git a/src/tqec/templates/base.py b/src/tqec/templates/base.py index 4096a6b6..d09e6d91 100644 --- a/src/tqec/templates/base.py +++ b/src/tqec/templates/base.py @@ -81,7 +81,9 @@ def __init__( default_x_increment, default_y_increment ) - def _check_plaquette_number(self, plaquette_indices: ty.Sequence[int]) -> None: + def _check_plaquette_number( + self, plaquette_indices: ty.Sequence[int], expected_plaquettes_number: int + ) -> None: """Checks the number of provided plaquettes. This method should be called to check that the number of plaquette indices @@ -89,15 +91,16 @@ def _check_plaquette_number(self, plaquette_indices: ty.Sequence[int]) -> None: Args: plaquette_indices: the indices provided to the ``instantiate`` method. - + expected_plaquettes_number: the number of plaquettes expected in + ``plaquette_indices``. Raises: TQECError: when there is not enough plaquette indices to instantiate the ``Template`` instance. """ - if len(plaquette_indices) < self.expected_plaquettes_number: + if len(plaquette_indices) < expected_plaquettes_number: raise TQECException( f"{self.__class__.__name__}.instanciate needs " - f"{self.expected_plaquettes_number} plaquettes, but only " + f"{expected_plaquettes_number} plaquettes, but only " f"{len(plaquette_indices)} were provided." )