Repository: QCHackers/tqec
Pull request #137 (2024-02-14 09:36:29)

Refactor Plaquette hierarchy
This PR try to address #136.

nelimee: Note: removing the `PlaquetteList` class made a few notebooks fail. `PlaquetteList` has always been too specialised to be in the main library in my opinion (only useful for simple memory experiments), so I will change the notebooks accordingly.
review-notebook-app[bot]: Check out this pull request on&nbsp; <a href="https://app.reviewnb.com/QCHackers/tqec/pull/137"><img align="absmiddle"  alt="ReviewNB" height="28" class="BotMessageButtonImage" src="https://raw.githubusercontent.com/ReviewNB/support/master/images/button_reviewnb.png"/></a> 

 See visual diffs & provide feedback on Jupyter Notebooks. 

---

 <i>Powered by <a href='https://www.reviewnb.com/?utm_source=gh'>ReviewNB</a></i>
nelimee: The notebook [`move_qubit_along_a_line.ipynb`](https://github.com/QCHackers/tqec/blob/main/notebooks/move_qubit_along_a_line.ipynb) should still be updated to the new plaquette hierarchy, but the other notebooks are working as expected. 
smburdick: @nelimee Would you like my review on this?
nelimee: I am still missing the rewriting of one notebook, I am having a hard time getting the observable correct, but everything else should be near final so if you have the time, your review is more than welcome 😊
The more opinions we have, the better it is! 
nelimee: With this last commit, I did everything I planned with this PR. I am ready to discuss/make changes according to your reviews :) 
smburdick: > With this last commit, I did everything I planned with this PR. I am ready to discuss/make changes according to your reviews :)

I'll see what I can do. I would like to familiarize myself with the backend more.
nelimee: > I'll see what I can do. I would like to familiarize myself with the backend more.

Feel free to ask me questions if you have any, we can also schedule a meeting like last time :)
nelimee: I will also, either this week-end or at the beginning of next week, refactor the different measurement plaquettes. All the `{XX,ZZ,XXXX,ZZZZ}FinalMeasurementPlaquette` will be replaced by two plaquettes `MeasurementRoundedPlaquette` and `MeasurementSquarePlaquette` in a new `tqec.plaquettes.library.memory` module. 

With the exception of that change (that is simply a code factorisation step), this PR should still be considered finalised and ready to be reviewed.
inmzhang: I've gone through most of the refactoring changes in the associated PR #100 and initially had few opinions. I will revisit this tomorrow for a closer look.
nelimee: Following the last commit, I have a few conceptual questions. 

First, currently, two "features" of a `Plaquette` may be passed down by inheritance: 
1. its shape: is it square or rounded
2. its effect: is it a `XX` syndrome measurement, `XXXX`, `ZZ`, `ZZZZ`?

Initially, the `RoundedPlaquette` and `SquarePlaquette` have been introduced to have a common definition **and order** of the data qubits the plaquette should operate on, as well as to have helper methods to recover the correct data qubits in the case of `RoundedPlaquette` instances. 

Now, I really think that it is more meaningful to have `XXMemoryPlaquette` (that should probably be renamed `XXSyndromeMeasurementPlaquette`) inherit from a `PauliMemoryPlaquette` (should be renamed too if introduced) than from a `RoundedPlaquette`.

Any opinion on that?
inmzhang: > Now, I really think that it is more meaningful to have XXMemoryPlaquette (that should probably be renamed XXSyndromeMeasurementPlaquette) inherit from a PauliMemoryPlaquette (should be renamed too if introduced) than from a RoundedPlaquette.

LGTM. This kind of syndrome measurements should have the same structure at least at the stabilizer level. One comment which is not quite related to this PR, on the native gateset we used in the circuit implementation: While you have chosen the CNOT gate, I can think of another two ways to implement the controlled-pauli gates:

1. Use A control B gates, where A/B are control and target basis for the gate. This has the benefits of completely removing the basis change gates and is natively supported by `stim`. In the simple X/Z plaquette setting, this might be overkilling. But it can be useful in more complicated pauli syndrome measurements. However, these gates seem nontrivial in `cirq` and will need to be transpiled to some experimental native gateset if we want to simulate for experiment setups.
2. Use CZ with basis-change single qubit gates. This has the benefits of being symmetry and is native to superconducting qubits(and some other systems). But this can complicate the simple X/Z syndrome circuits a little bit.

I'm still fine with the CNOT, which can be transpiled to CZ gateset in the post circuit transformation.
nelimee: I re-tested and re-read everything, this PR was a little bit too large, thanks @inmzhang for reviewing! I'll adapt #130 and merge as soon as possible. 

Patch:
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 .noise_models import (
 )
 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 @@ class CircuitMeasurementMap:
         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 @@ class CircuitMeasurementMap:
 
         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 @@ class ShiftCoords(cirq.Operation):
     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 @@ class RelativeMeasurementData:
                 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 @@ class RelativeMeasurementsRecord(cirq.Operation):
         """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/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 @@ class SquarePlaquette(Plaquette):
             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 @@ class SquarePlaquette(Plaquette):
     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 @@ class RoundedPlaquette(Plaquette):
         PlaquetteOrientation.UP orientation will have its two qubits ordered as
         ```text
           -------
-         /       \
+         /       \\
         | 1     2 |
         |---------|
         ```
@@ -148,19 +165,8 @@ class RoundedPlaquette(Plaquette):
                 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 @@ class RoundedPlaquette(Plaquette):
         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 @@ class AlternatingRectangleTemplate(Template):
         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 @@ class AlternatingRectangleTemplate(Template):
         return 2
 
 
+@ty.final
 class RawRectangleTemplate(Template):
     def __init__(
         self,
@@ -108,6 +109,10 @@ class RawRectangleTemplate(Template):
         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 @@ class RawRectangleTemplate(Template):
         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 @@ class AlternatingCornerSquareTemplate(Template):
         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 @@ class Template(JSONEncodable):
             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 @@ class Template(JSONEncodable):
 
         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."
             )
 


Test patch:
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.detectors.operation import make_shift_coords
 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()