{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Validation of Syndrome Extraction Circuits" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import loom.validator\n", "from loom.eka import Block, Stabilizer, PauliOperator, Channel, Circuit" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook demonstrates how to use the `loom.validator` for syndrome extraction circuits\n", "describing the full process." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Steane Code Reichardt circuit" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Reichardt proposed a circuit that measures in a FT manner the syndrome of Steane's code.\n", "\n", "Reference: " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Code definition" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The code stabilizers are:\n", "\n", "![Steane Code](../_static/validator_notebook_images/steane_code.png)\n", "\n", "Note that the indexing we will be using starts from 0 !\n", "\n", "Let's create the `Block` object describing\n", "this code.\n", "\n", "Simultaneously, let's keep the stabilizers in a format we can access them later on." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# Define qubit layout\n", "# For demonstration purposes we use repeating tuple rather than a simple int\n", "qubit_labels = [(0,), (1,), (2,), (3,), (4,), (5,), (6,)]\n", "\n", "# Define stabilizers within a dictionary\n", "stabs_dict: dict[str, list[Stabilizer]] = {\n", " \"X\": [],\n", " \"Z\": [],\n", "}\n", "\n", "\n", "# First set of X, Z stabilizers\n", "stab_qub_labels = [(0,), (2,), (4,), (6,)]\n", "# make the stabilizers\n", "stabs_dict[\"X\"] += [Stabilizer(\"XXXX\", stab_qub_labels)]\n", "stabs_dict[\"Z\"] += [Stabilizer(\"ZZZZ\", stab_qub_labels)]\n", "\n", "# Second set of X, Z stabilizers\n", "stab_qub_labels = [(1,), (2,), (5,), (6,)]\n", "# make the stabilizers\n", "stabs_dict[\"X\"] += [Stabilizer(\"XXXX\", stab_qub_labels)]\n", "stabs_dict[\"Z\"] += [Stabilizer(\"ZZZZ\", stab_qub_labels)]\n", "\n", "# Third set of X, Z stabilizers\n", "stab_qub_labels = [(3,), (4,), (5,), (6,)]\n", "# make the stabilizers\n", "stabs_dict[\"X\"] += [Stabilizer(\"XXXX\", stab_qub_labels)]\n", "stabs_dict[\"Z\"] += [Stabilizer(\"ZZZZ\", stab_qub_labels)]\n", "\n", "# Define the Block\n", "steane_block = Block(\n", " stabilizers=[s for s_list in stabs_dict.values() for s in s_list],\n", " logical_x_operators=[PauliOperator(\"X\" * 7, qubit_labels)],\n", " logical_z_operators=[PauliOperator(\"Z\" * 7, qubit_labels)],\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Circuit testing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The proposed circuit that measures half of those stabilizers is:\n", "\n", "![Reichardt Circuit](../_static/validator_notebook_images/reichardt_circuit.png)\n", "\n", "Note that the indexing we will be using starts from 0 !" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# Define data qubits and necessary mappings\n", "n_data = steane_block.n_data_qubits\n", "dqubit_channels = [Channel(label=str(qub)) for qub in steane_block.data_qubits]\n", "\n", "\n", "# Define ancilla qubits\n", "n_ancillas = 3\n", "aqubit_channels = [Channel(label=f\"ancilla_qubit_{i}\") for i in range(n_ancillas)]\n", "\n", "# Define classical channels for ancilla qubits\n", "c_channels = [Channel(type=\"classical\", label=f\"c_{i}_0\") for i in range(n_ancillas)]" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "# Construct the circuit\n", "syndrome_extraction_circ = Circuit(\n", " \"first_round\",\n", " circuit=[\n", " [Circuit(\"H\", channels=[aqubit_channels[0]])],\n", " [\n", " Circuit(\"CNOT\", channels=[aqubit_channels[0], dqubit_channels[4]]),\n", " Circuit(\"CNOT\", channels=[dqubit_channels[6], aqubit_channels[1]]),\n", " Circuit(\"CNOT\", channels=[dqubit_channels[5], aqubit_channels[2]]),\n", " ],\n", " [Circuit(\"CNOT\", channels=[aqubit_channels[0], aqubit_channels[2]])],\n", " [\n", " Circuit(\"CNOT\", channels=[aqubit_channels[0], dqubit_channels[0]]),\n", " Circuit(\"CNOT\", channels=[dqubit_channels[4], aqubit_channels[1]]),\n", " Circuit(\"CNOT\", channels=[dqubit_channels[1], aqubit_channels[2]]),\n", " ],\n", " [\n", " Circuit(\"CNOT\", channels=[aqubit_channels[0], dqubit_channels[2]]),\n", " Circuit(\"CNOT\", channels=[dqubit_channels[3], aqubit_channels[1]]),\n", " Circuit(\"CNOT\", channels=[dqubit_channels[6], aqubit_channels[2]]),\n", " ],\n", " [Circuit(\"CNOT\", channels=[aqubit_channels[0], aqubit_channels[1]])],\n", " [\n", " Circuit(\"CNOT\", channels=[aqubit_channels[0], dqubit_channels[6]]),\n", " Circuit(\"CNOT\", channels=[dqubit_channels[5], aqubit_channels[1]]),\n", " Circuit(\"CNOT\", channels=[dqubit_channels[2], aqubit_channels[2]]),\n", " ],\n", " [Circuit(\"H\", channels=[aqubit_channels[0]])],\n", " [\n", " Circuit(\"Measurement\", channels=[aqubit_channels[0], c_channels[0]]),\n", " Circuit(\"Measurement\", channels=[aqubit_channels[1], c_channels[1]]),\n", " Circuit(\"Measurement\", channels=[aqubit_channels[2], c_channels[2]]),\n", " ],\n", " ],\n", ")" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "# We need to specify the stabilizers that were measured in the order that they\n", "# were measured.\n", "# Dictionary has as key the index of the measurement operation (considering only\n", "# measurement operations) and as value the stabilizer.\n", "measurement_to_stabilizer_map = {\n", " c_channels[0].label: stabs_dict[\"X\"][0],\n", " c_channels[1].label: stabs_dict[\"Z\"][2],\n", " c_channels[2].label: stabs_dict[\"Z\"][1],\n", "}" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "DebugData: valid = True\n" ] } ], "source": [ "# Run loom.validator and check result\n", "debug_dict = loom.validator.is_syndrome_extraction_circuit_valid(\n", " syndrome_extraction_circ,\n", " steane_block,\n", " measurement_to_input_stabilizer_map=measurement_to_stabilizer_map,\n", ")\n", "\n", "print(debug_dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Sanity checks" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 1. Indicate that other stabilizers were measured" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "# Reminder of stabs measured\n", "measurement_to_stabilizer_map = {\n", " c_channels[0].label: stabs_dict[\"X\"][0],\n", " c_channels[1].label: stabs_dict[\"Z\"][2],\n", " c_channels[2].label: stabs_dict[\"Z\"][1],\n", "}\n", "\n", "measurement_to_stabilizer_map_wrong = {\n", " c_channels[0].label: stabs_dict[\"X\"][0],\n", " c_channels[1].label: stabs_dict[\"Z\"][0],\n", " c_channels[2].label: stabs_dict[\"Z\"][1],\n", "}" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "DebugData: valid = False\n", "----------------------------------------\n", "StabilizerMeasurementCheck: valid = False\n", "message: Some measurement(s) did not measure the assigned stabilizer.\n", "output: \n", "- Expected vs Measured Stabilizers:\n", "Measurement c_1_0: Expected Z_(0,) Z_(2,) Z_(4,) Z_(6,), Measured: Z_(3,) Z_(4,) Z_(5,) Z_(6,)\n" ] } ], "source": [ "# Run loom.validator and check result\n", "debug_dict = loom.validator.is_syndrome_extraction_circuit_valid(\n", " syndrome_extraction_circ,\n", " steane_block,\n", " measurement_to_input_stabilizer_map=measurement_to_stabilizer_map_wrong,\n", ")\n", "\n", "print(debug_dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 2. Adding CNOT between two data qubits" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "# CNOT between data qubits\n", "cy_d0d1 = Circuit(\n", " \"CNOT\",\n", " channels=[dqubit_channels[0], dqubit_channels[6]],\n", ")\n", "\n", "# Construct full circuit\n", "circuit_w_cnot = Circuit(\n", " \"circuit_with_added_cnot\",\n", " circuit=syndrome_extraction_circ.circuit + ((cy_d0d1,),),\n", ")" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "DebugData: valid = False\n", "----------------------------------------\n", "CodeStabilizerCheck: valid = False\n", "message: Some code stabilizer(s) were not found in the output.\n", "output: \n", "- Missing Stabilizers:\n", "X_(0,) X_(2,) X_(4,) X_(6,)\n", "Z_(0,) Z_(2,) Z_(4,) Z_(6,)\n", "Z_(1,) Z_(2,) Z_(5,) Z_(6,)\n", "Z_(3,) Z_(4,) Z_(5,) Z_(6,)\n", "----------------------------------------\n", "LogicalOperatorCheck: valid = False\n", "message: Some logical states were not transformed correctly.\n", "output: \n", "- Failed Logical State Transformations:\n", "Initial: LogicalState(('+Z0',)), Allowed Finals: (LogicalState(('+Z0',)),), Actual: None\n", "Initial: LogicalState(('+X0',)), Allowed Finals: (LogicalState(('+X0',)),), Actual: None\n" ] } ], "source": [ "# Run loom.validator and check result\n", "debug_dict = loom.validator.is_syndrome_extraction_circuit_valid(\n", " circuit_w_cnot,\n", " steane_block,\n", " measurement_to_input_stabilizer_map=measurement_to_stabilizer_map,\n", ")\n", "\n", "print(debug_dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 3. Performing a logical operation" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "# Logical X\n", "x_ops = tuple(\n", " (\n", " Circuit(\n", " \"X\",\n", " channels=[dqubit_channels[i]],\n", " ),\n", " )\n", " for i in range(7)\n", ")\n", "\n", "# Construct full circuit\n", "circuit_w_log_x = Circuit(\n", " \"circuit_with_log_x\",\n", " circuit=syndrome_extraction_circ.circuit + x_ops,\n", ")" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "DebugData: valid = False\n", "----------------------------------------\n", "LogicalOperatorCheck: valid = False\n", "message: Some logical states were not transformed correctly.\n", "output: \n", "- Failed Logical State Transformations:\n", "Initial: LogicalState(('+Z0',)), Allowed Finals: (LogicalState(('+Z0',)),), Actual: LogicalState(('-Z0',))\n" ] } ], "source": [ "# Run loom.validator and check result\n", "debug_dict = loom.validator.is_syndrome_extraction_circuit_valid(\n", " circuit_w_log_x,\n", " steane_block,\n", " measurement_to_stabilizer_map,\n", ")\n", "\n", "print(debug_dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 4. Applying a destabilizer operation" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "# We can see for ourselves that Z3Z7 (reichard indexing) -> Z2Z6 our indexing\n", "# is the destabilizer of the first stabilizer (commuting with all stab + log ops)\n", "z_destab_ops = tuple(\n", " (\n", " Circuit(\n", " \"Z\",\n", " channels=[dqubit_channels[i]],\n", " ),\n", " )\n", " for i in [2, 6]\n", ")\n", "\n", "# Construct full circuit\n", "circuit_w_destab = Circuit(\n", " \"circuit_w_destab\",\n", " circuit=syndrome_extraction_circ.circuit + z_destab_ops,\n", ")" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "DebugData: valid = False\n", "----------------------------------------\n", "CodeStabilizerCheck: valid = False\n", "message: Some code stabilizer(s) were not found in the output.\n", "output: \n", "- Stabilizers with Incorrect Parity:\n", "X_(3,) X_(4,) X_(5,) X_(6,)\n" ] } ], "source": [ "# Run loom.validator and check result\n", "debug_dict = loom.validator.is_syndrome_extraction_circuit_valid(\n", " circuit_w_destab,\n", " steane_block,\n", " measurement_to_stabilizer_map,\n", ")\n", "\n", "print(debug_dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Repetition code with binary operations" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![Repetition Code Circuit](../_static/validator_notebook_images/repetition_code_adaptive_circuit.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We shall be using 2 extra ancilla qubits which will act as classical registers.\n", "\n", "XOR will be found by performing 2 CNOTS after measuring the ancilla qubits." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "# Define Block\n", "\n", "rep_code_block = Block(\n", " stabilizers=[Stabilizer(\"ZZ\", [(0,), (1,)]), Stabilizer(\"ZZ\", [(1,), (2,)])],\n", " logical_x_operators=[PauliOperator(\"XXX\", [(0,), (1,), (2,)])],\n", " logical_z_operators=[PauliOperator(\"ZZZ\", [(0,), (1,), (2,)])],\n", ")" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "# Define circuit\n", "# Define data qubits and necessary mappings\n", "n_data = rep_code_block.n_data_qubits\n", "dqubits = [Channel(label=str(q)) for q in rep_code_block.data_qubits]\n", "\n", "# Define ancilla qubits\n", "n_ancillas = 3\n", "aqubit_channels = [Channel(label=f\"ancilla_qubit_{i}\") for i in range(n_ancillas)]\n", "# and classical channels for ancilla qubits\n", "ac_channels = [\n", " Channel(type=\"classical\", label=f\"c_{a_chan.label}\") for a_chan in aqubit_channels\n", "]\n", "\n", "# Define ancillas that act as classical register\n", "n_creg_qubits = 2\n", "creg_qubit_channels = [\n", " Channel(label=f\"ancilla_as_classical_{i}\") for i in range(n_creg_qubits)\n", "]\n", "# and classical channels for these classical register qubits\n", "cregc_channels = [\n", " Channel(type=\"classical\", label=f\"c_{creg_chan.label}\")\n", " for creg_chan in creg_qubit_channels\n", "]" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "# Construct circuit\n", "rep_code_syndrome_extraction = Circuit(\n", " \"rep_code_via_ghz\",\n", " circuit=[\n", " Circuit(\"H\", channels=[aqubit_channels[0]]),\n", " Circuit(\"CNOT\", channels=[aqubit_channels[0], aqubit_channels[1]]),\n", " Circuit(\"CNOT\", channels=[aqubit_channels[0], aqubit_channels[2]]),\n", " *(Circuit(\"CNOT\", channels=[dqubits[i], aqubit_channels[i]]) for i in range(3)),\n", " *(\n", " Circuit(\"Measurement\", channels=[aqubit_channels[i], ac_channels[i]])\n", " for i in range(3)\n", " ),\n", " *(\n", " Circuit(\"CNOT\", channels=[aqubit_channels[i], creg_qubit_channels[j]])\n", " for j in range(2)\n", " for i in range(j, j + 2)\n", " ),\n", " *(\n", " Circuit(\"Measurement\", channels=[creg_qubit_channels[i], cregc_channels[i]])\n", " for i in range(2)\n", " ),\n", " ],\n", ")" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "# Define where the stabilizers will be found\n", "# The first three measurements are the ancillas\n", "# The fourth and fifth measurements are the stabilizer values\n", "rep_measurement_to_stabilizer_map = {\n", " cregc_channels[0].label: rep_code_block.stabilizers[0],\n", " cregc_channels[1].label: rep_code_block.stabilizers[1],\n", "}" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "DebugData: valid = True\n" ] } ], "source": [ "# Run loom.validator and check result\n", "debug_dict = loom.validator.is_syndrome_extraction_circuit_valid(\n", " rep_code_syndrome_extraction,\n", " rep_code_block,\n", " rep_measurement_to_stabilizer_map,\n", ")\n", "\n", "print(debug_dict)" ] } ], "metadata": { "kernelspec": { "display_name": "loom-py3.12", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.7" } }, "nbformat": 4, "nbformat_minor": 2 }