{ "cells": [ { "cell_type": "markdown", "id": "c0988d87", "metadata": {}, "source": [ "\n", "# Verifying Logical Clifford Gadgets\n", "\n", "**Download this notebook - {nb-download}`clifford_verification.ipynb`**\n", "\n", "Motivation: Logical Cliffords can have non-obvious implementations, especially for $k>1$ codes. We want to be able to validate implementations and catch bugs in our logical gadgets.\n", "\n", "For example, see Table 2 from [Simple logical quantum computation with concatenated symplectic double codes](https://arxiv.org/pdf/2510.18753). Below is a section of this table that shows the physical implementation and logical action for \n", "\n", "![](./images/non_css_4_2_2_logical_cliffords1.png){w=240px align=center}" ] }, { "cell_type": "markdown", "id": "ee5ac7c5", "metadata": {}, "source": [ "\n", "\n", "* The {py:mod}`guppyft.verify` module checks logical Clifford semantics against physical implementation, using the {py:func}`~guppyft.verify.valid_clifford_implementation` function.\n", "* The tool works for $k>1$ codes and also operations between two code blocks.\n", "* This approach is fairly general and works for any stabilizer code. Includes non-CSS codes." ] }, { "cell_type": "markdown", "id": "20489171", "metadata": {}, "source": [ "We will specify the logical semantics with a Guppy function which acts on an array of $k$ qubits. Let's see an example for the $[[7, 1, 3]]$ Steane code. The action of a logical Hadamard in the Steane code is as follows..." ] }, { "cell_type": "code", "execution_count": 1, "id": "bdcae564", "metadata": {}, "outputs": [], "source": [ "from guppylang import guppy\n", "from guppylang.std.builtins import array\n", "from guppylang.std.quantum import qubit, h\n", "\n", "\n", "@guppy\n", "def steane_specify_h(qs: array[qubit, 1]) -> None:\n", " h(qs[0])" ] }, { "cell_type": "markdown", "id": "791fa089", "metadata": {}, "source": [ "We can provide the implementation of the logical operation as a function which acts on an array of $n$ *physical* qubits." ] }, { "cell_type": "code", "execution_count": 2, "id": "463835e5", "metadata": {}, "outputs": [], "source": [ "@guppy\n", "def steane_impl_h(block: array[qubit, 7]) -> None:\n", " for i in range(len(block)):\n", " h(block[i])" ] }, { "cell_type": "markdown", "id": "641f24fb", "metadata": {}, "source": [ "The verifier then calculates the Clifford tableau of both of them, and checks that they are equivalent. Since the logical specification is in terms of $k$ logical qubits and the implementation is in terms of $n$ physical qubits, we need to expand the tableau of $k$ logical qubits to $n$ physical, using the definition of the stabilizer code. The definition of the stabilizer code tells us how to replace a logical Pauli with its physical Pauli string realization, as well as which additional Pauli strings we need to introduce to account for the code's stabilizer group." ] }, { "cell_type": "markdown", "id": "5961f2f4", "metadata": {}, "source": [ "## Providing the stabilizer code definition\n", "\n", "Before we can get to verifying logical Cliffords, we need a way to specify the key properties of a stabilizer code. We provide a {py:class}`~guppyft.code_def.StabilizerCode` dataclass which specifies the $[[n, k, d]]$ code parameters as integers as well as the stabilizer generators and logical operators.\n", "\n", "```python\n", "@dataclass(frozen=True)\n", "class StabilizerCode:\n", " n_physical_qubits: int\n", " n_logical_qubits: int\n", " distance: int\n", " generators: pauli.SignTermSet\n", " x_logicals: pauli.SignTerms\n", " z_logicals: pauli.SignTerms\n", "```\n", "\n", "The stabilizer generators and logical operators are represented using types from [Zixy](https://github.com/Quantinuum/zixy), a library which provides memory efficient Pauli operator implementations in Rust.\n", "\n", "A {py:class}`~guppyft.code_def.StabilizerCode` definition can be given by specifying the fields directly. However its usually easier to use the {py:meth}`~guppyft.code_def.StabilizerCode.from_python_strings` utility method.\n", "\n", "Let's provide definitions for the Steane code." ] }, { "cell_type": "code", "execution_count": 3, "id": "f67df228", "metadata": {}, "outputs": [], "source": [ "from guppyft.code_def import StabilizerCode\n", "\n", "STEANE_DEF = StabilizerCode.from_python_strings(\n", " n_physical_qubits=7,\n", " n_logical_qubits=1,\n", " distance=3,\n", " generators=[\"XXXXIII\", \"IXXIXXI\", \"IIXXIXX\", \"ZZZZIII\", \"IZZIZZI\", \"IIZZIZZ\"],\n", " x_logicals=[\"XXXXXXX\"],\n", " z_logicals=[\"ZZZZZZZ\"],\n", ")" ] }, { "cell_type": "markdown", "id": "ac323fe7", "metadata": {}, "source": [ "We can also provide a code definition for the non-CSS $[[4, 2, 2]]$ code which is discussed in the paper entitled \"Simple logical quantum computation with concatenated symplectic double codes\" (N. Berthusen and E. Durso-Sabina - https://arxiv.org/pdf/2510.18753)\n", "\n", "With non-CSS codes, each stabilizer generator can be made ofa mixture of $X$ and $Z$ operators. The same is true of the logical operators." ] }, { "cell_type": "code", "execution_count": 4, "id": "6c251e60", "metadata": {}, "outputs": [], "source": [ "NON_CSS_4_2_2 = StabilizerCode.from_python_strings(\n", " n_physical_qubits=4,\n", " n_logical_qubits=2,\n", " distance=2,\n", " generators=[\"XZZX\", \"ZXXZ\"],\n", " x_logicals=[\"ZIXI\", \"IZIX\"],\n", " z_logicals=[\"IZZI\", \"ZIIZ\"],\n", ")" ] }, { "cell_type": "markdown", "id": "cbeb1c67", "metadata": {}, "source": [ "There is some basic `__post_init__` validation to check we are providing reasonable {py:class}`~guppyft.code_def.StabilizerCode` definitions. \n", "\n", "For example, all of the stabilizer generators must commute with one another. If they do not mutually commute, then the code definition is rejected." ] }, { "cell_type": "markdown", "id": "019b85fa-c20c-409a-b40d-1ce5c283ae66", "metadata": {}, "source": [ "## Verifying the implementation of logical Cliffords\n", "\n", "Now we can get on to verifying logical operations. We'll start by verifying some operations functions in the Steane code. This is done with the {py:func}`~guppyft.verify.valid_clifford_implementation` function." ] }, { "cell_type": "code", "execution_count": 5, "id": "45790554", "metadata": {}, "outputs": [], "source": [ "from guppyft.verify import valid_clifford_implementation" ] }, { "cell_type": "markdown", "id": "ff4ed1ac", "metadata": {}, "source": [ "### Basic examples for the Steane code" ] }, { "cell_type": "markdown", "id": "0131f107", "metadata": {}, "source": [ "First, let's check the logical Hadamard implementation we defined above." ] }, { "cell_type": "code", "execution_count": 6, "id": "cbe3a888", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "valid_clifford_implementation(\n", " steane_specify_h, steane_impl_h, STEANE_DEF\n", ") # True => implementation is valid" ] }, { "cell_type": "markdown", "id": "7e8bc44f", "metadata": {}, "source": [ "We can also verify operators between two code blocks. As an example, let's verify the implementation of the transversal CX in the Steane code." ] }, { "cell_type": "code", "execution_count": 7, "id": "a1cc505d", "metadata": {}, "outputs": [], "source": [ "from guppylang.std.quantum import cx\n", "\n", "\n", "@guppy\n", "def steane_specify_cx(\n", " first_block: array[qubit, 1], second_block: array[qubit, 1]\n", ") -> None:\n", " cx(first_block[0], second_block[0])\n", "\n", "\n", "@guppy\n", "def steane_impl_cx(first_block: array[qubit, 7], second_block: array[qubit, 7]) -> None:\n", " for i in range(len(first_block)):\n", " cx(first_block[i], second_block[i])" ] }, { "cell_type": "code", "execution_count": 8, "id": "9fa81861", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "valid_clifford_implementation(\n", " steane_specify_cx, steane_impl_cx, STEANE_DEF\n", ") # Transversal CX impl is valid." ] }, { "cell_type": "markdown", "id": "902ee2ae", "metadata": {}, "source": [ "We can also detect invalid implementations. If we try to implement a logical $S$ gate in the Steane code by applying physical $S$ across across all seven physical qubits, we get `False` indicating that our implementation of logical $S$ is wrong. " ] }, { "cell_type": "code", "execution_count": 9, "id": "576ba8af", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from guppylang.std.quantum import s\n", "\n", "\n", "@guppy\n", "def steane_specify_s(block: array[qubit, 1]) -> None:\n", " s(block[0])\n", "\n", "\n", "@guppy\n", "def steane_impl_s_incorrect(block: array[qubit, 7]) -> None:\n", " for i in range(len(block)):\n", " s(block[i])\n", "\n", "\n", "valid_clifford_implementation(\n", " steane_specify_s, steane_impl_s_incorrect, STEANE_DEF\n", ") # Invalid!" ] }, { "cell_type": "markdown", "id": "5388272e", "metadata": {}, "source": [ "It turns out the correct way to implement logical $S$ in the Steane code is by applying $S^\\dagger$ across the physical qubits.\n" ] }, { "cell_type": "code", "execution_count": 10, "id": "63b88855", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from guppylang.std.quantum import sdg\n", "\n", "\n", "@guppy\n", "def steane_impl_s_corrected(block: array[qubit, 7]) -> None:\n", " for i in range(len(block)):\n", " sdg(block[i])\n", "\n", "\n", "valid_clifford_implementation(\n", " steane_specify_s, steane_impl_s_corrected, STEANE_DEF\n", ") # Now valid!" ] }, { "cell_type": "markdown", "id": "1ceff39b-a08c-4c09-8461-52f7a856bc43", "metadata": {}, "source": [ "### Examples for $k>1$ codes\n", "So far, the examples with the Steane code have just been simple transversal operations. However, logical Clifford operations for $k > 1$ codes can also be verified. Let's consider the $k = 2$ CSS $[[ 4 , 2 , 2 ]]$ code to see this" ] }, { "cell_type": "code", "execution_count": 11, "id": "979924bb", "metadata": {}, "outputs": [], "source": [ "CSS_4Q_DEF = StabilizerCode.from_python_strings(\n", " n_physical_qubits=4,\n", " n_logical_qubits=2,\n", " distance=2,\n", " generators=[\"XXXX\", \"ZZZZ\"],\n", " x_logicals=[\"XXII\", \"XIXI\"],\n", " z_logicals=[\"IZIZ\", \"IIZZ\"],\n", ")" ] }, { "cell_type": "markdown", "id": "ae3193fa", "metadata": {}, "source": [ "In this $[[4, 2, 2]]$ code, we can implement a logical CX gate (intrablock) by simply swapping the physical qubits. " ] }, { "cell_type": "code", "execution_count": 12, "id": "be0117c2-ef8c-42d7-923a-6454a7669cd4", "metadata": {}, "outputs": [], "source": [ "from guppylang.std.mem import mem_swap\n", "\n", "\n", "@guppy\n", "def specify_intra_block_cx(block: array[qubit, 2]) -> None:\n", " cx(block[0], block[1])\n", "\n", "\n", "@guppy\n", "def implement_intra_block_cx(block: array[qubit, 4]) -> None:\n", " mem_swap(block[3], block[1])" ] }, { "cell_type": "code", "execution_count": 13, "id": "afb8c35a-b586-4aee-94eb-05299db5a3ac", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "valid_clifford_implementation(\n", " specify_intra_block_cx, implement_intra_block_cx, CSS_4Q_DEF\n", ")" ] }, { "cell_type": "markdown", "id": "ec48db5d", "metadata": {}, "source": [ "We can also verify the implementation of logical SWAP in the non-CSS $[[4, 2, 2]]$ code. This is implemented by applying the Hadamard gate to all of the physical qubits." ] }, { "cell_type": "code", "execution_count": 14, "id": "c359fcf5-0590-4f91-8b5f-c58ab9230da8", "metadata": {}, "outputs": [], "source": [ "@guppy\n", "def specify_swap_non_css(block: array[qubit, 2]) -> None:\n", " mem_swap(block[0], block[1])\n", "\n", "\n", "@guppy\n", "def implement_swap_non_css(block: array[qubit, 4]) -> None:\n", " for i in range(len(block)):\n", " h(block[i])" ] }, { "cell_type": "code", "execution_count": 15, "id": "3e1e08d5-4f14-4c0e-83f1-3293fd72af83", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "valid_clifford_implementation(\n", " specify_swap_non_css,\n", " implement_swap_non_css,\n", " NON_CSS_4_2_2,\n", ")" ] }, { "cell_type": "markdown", "id": "edf26c01", "metadata": {}, "source": [ "## Verifying preparation of logical Pauli eigenstates" ] }, { "cell_type": "markdown", "id": "d3e2fbfd", "metadata": {}, "source": [ "The testing framework also allows us to verify state preparation. For example we can check preparation of logical \n", "$|0\\rangle$ $\\big(|0\\rangle_{L}\\big)$ in the $[[4, 2, 2]]$ code as follows." ] }, { "cell_type": "code", "execution_count": 16, "id": "a37ac88a", "metadata": {}, "outputs": [], "source": [ "from guppyft.verify import valid_stabilizer_state_preparation\n", "\n", "\n", "@guppy\n", "def specify_zero_state() -> array[qubit, 2]:\n", " return array(qubit() for _ in range(2))\n", "\n", "\n", "@guppy\n", "def implement_non_ft_zero_state() -> array[qubit, 4]:\n", " block = array(qubit() for _ in range(4))\n", "\n", " h(block[0])\n", " cx(block[0], block[1])\n", " cx(block[0], block[2])\n", " cx(block[0], block[3])\n", " return block" ] }, { "cell_type": "code", "execution_count": 17, "id": "9cebbfd9", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "valid_stabilizer_state_preparation(\n", " specify_zero_state, implement_non_ft_zero_state, CSS_4Q_DEF\n", ")" ] }, { "cell_type": "markdown", "id": "ec9aa79d-b4a6-4f1a-a5bf-0999efd30ba2", "metadata": {}, "source": [ "## Explaining the verifier step by step\n", "\n", "Let's go back to the logical Hadamard in Steane. How do we work out that logical Hadamard can be implemented by broadcasting a Hadamard gate across all seven physical qubits?\n", "\n", "\n", "Firstly we calculate the stabilizers of a $2k$ qubit Choi state which encodes the logical Hadamard." ] }, { "cell_type": "code", "execution_count": 18, "id": "64ba6cf1-663e-4b15-800e-048075306531", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ComponentCoefficient
0X0 Z1+1
1Z0 X1+1
\n", "
" ], "text/plain": [ " Component Coefficient\n", "0 X0 Z1 +1\n", "1 Z0 X1 +1" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from guppyft.verify._verify import _compute_stabilizers_single_block_unitary\n", "from guppyft.verify._expansion import get_expanded_stabilizer_set\n", "from guppyft.code_def import _identity_code\n", "\n", "\n", "# Get the 2k stabilizers for the 2k qubit Choi state encoding the logical operation.\n", "semantic_choi_stabilizers = _compute_stabilizers_single_block_unitary(\n", " code=_identity_code(k=1),\n", " clifford_func=steane_specify_h,\n", " num_selene_qubits=2 * STEANE_DEF.n_logical_qubits,\n", ")\n", "semantic_choi_stabilizers.to_dataframe()" ] }, { "cell_type": "markdown", "id": "620e2d5c", "metadata": {}, "source": [ "Notice how the stabilizers are closely related to the stabilizers of the Bell state $\\{X_0X_1, Z_0Z_1\\}$ but with a logical Hadamard applied to $X_1$ and $Z_1$." ] }, { "cell_type": "markdown", "id": "4e0c62eb-2aaa-4da5-8bc7-487b32d60d93", "metadata": {}, "source": [ "Next, we expand the $2k$ logical stabilizers to $2k$ stabilizers of size $2n$.\n", "We also add the $2(n-k)$ stabilizer generators of our code.\n", "For each code block there are $n-k$ generators, so $2$ blocks give us $2(n-k)$.\n", "We have $2k + 2(n-k) = 2n$ stabilizers in total.\n", "\n", "We perform this expansion using the definition of the logical operators for the particular {py:class}`~guppyft.code_def.StabilizerCode` we are working with. For the Steane code, we do the following expansion:\n", "\n", "$$\n", "X_L \\mapsto XXXXXXX\\, \\qquad Z_L \\mapsto ZZZZZZZ\\,.\n", "$$" ] }, { "cell_type": "code", "execution_count": 19, "id": "7dfc8b02-633f-4fd4-8f7d-89fe93aa78c4", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ComponentCoefficient
0X0 X1 X2 X3 X4 X5 X6 Z7 Z8 Z9 Z10 Z11 Z12 Z13+1
1Z0 Z1 Z2 Z3 Z4 Z5 Z6 X7 X8 X9 X10 X11 X12 X13+1
2X0 X1 X2 X3+1
3X1 X2 X4 X5+1
4X2 X3 X5 X6+1
5Z0 Z1 Z2 Z3+1
6Z1 Z2 Z4 Z5+1
7Z2 Z3 Z5 Z6+1
8X7 X8 X9 X10+1
9X8 X9 X11 X12+1
10X9 X10 X12 X13+1
11Z7 Z8 Z9 Z10+1
12Z8 Z9 Z11 Z12+1
13Z9 Z10 Z12 Z13+1
\n", "
" ], "text/plain": [ " Component Coefficient\n", "0 X0 X1 X2 X3 X4 X5 X6 Z7 Z8 Z9 Z10 Z11 Z12 Z13 +1\n", "1 Z0 Z1 Z2 Z3 Z4 Z5 Z6 X7 X8 X9 X10 X11 X12 X13 +1\n", "2 X0 X1 X2 X3 +1\n", "3 X1 X2 X4 X5 +1\n", "4 X2 X3 X5 X6 +1\n", "5 Z0 Z1 Z2 Z3 +1\n", "6 Z1 Z2 Z4 Z5 +1\n", "7 Z2 Z3 Z5 Z6 +1\n", "8 X7 X8 X9 X10 +1\n", "9 X8 X9 X11 X12 +1\n", "10 X9 X10 X12 X13 +1\n", "11 Z7 Z8 Z9 Z10 +1\n", "12 Z8 Z9 Z11 Z12 +1\n", "13 Z9 Z10 Z12 Z13 +1" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "expanded_semantic_stabilizers = get_expanded_stabilizer_set(\n", " semantic_choi_stabilizers, STEANE_DEF, num_blocks=2\n", ")\n", "\n", "expanded_semantic_stabilizers.to_dataframe()" ] }, { "cell_type": "markdown", "id": "904d1e7e", "metadata": {}, "source": [ "In order test two tableaux for equality, we have to convert to a suitable normal form. This is because two tableaux which look different can be equivalent up to multiplication by a Pauli stabilizer. We canonicalize the tableaux using the [SignTerms.canonicalize_all()](https://quantinuum.github.io/zixy/generated/zixy.qubit.pauli.SignTerms.html#zixy.qubit.pauli.SignTerms.canonicalize_all) method which uses a process similar to Gaussian elimination." ] }, { "cell_type": "code", "execution_count": 20, "id": "f05027e9-6009-484b-b11a-1c495cf246c5", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ComponentCoefficient
0X0 X3 X6 Z11 Z12 Z13+1
1X1 X3 X5 Z11 Z12 Z13+1
2X2 X3 X5 X6+1
3X4 X5 X6 Z11 Z12 Z13+1
4Z4 Z5 Z6 X7 X10 X13+1
5Z4 Z5 Z6 X8 X10 X12+1
6X9 X10 X12 X13+1
7Z4 Z5 Z6 X11 X12 X13+1
8Z0 Z3 Z4 Z5+1
9Z1 Z3 Z4 Z6+1
10Z2 Z3 Z5 Z6+1
11Z7 Z10 Z11 Z12+1
12Z8 Z10 Z11 Z13+1
13Z9 Z10 Z12 Z13+1
\n", "
" ], "text/plain": [ " Component Coefficient\n", "0 X0 X3 X6 Z11 Z12 Z13 +1\n", "1 X1 X3 X5 Z11 Z12 Z13 +1\n", "2 X2 X3 X5 X6 +1\n", "3 X4 X5 X6 Z11 Z12 Z13 +1\n", "4 Z4 Z5 Z6 X7 X10 X13 +1\n", "5 Z4 Z5 Z6 X8 X10 X12 +1\n", "6 X9 X10 X12 X13 +1\n", "7 Z4 Z5 Z6 X11 X12 X13 +1\n", "8 Z0 Z3 Z4 Z5 +1\n", "9 Z1 Z3 Z4 Z6 +1\n", "10 Z2 Z3 Z5 Z6 +1\n", "11 Z7 Z10 Z11 Z12 +1\n", "12 Z8 Z10 Z11 Z13 +1\n", "13 Z9 Z10 Z12 Z13 +1" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "expanded_semantic_stabilizers.canonicalize_all() # Canonicalize Clifford tableau\n", "expanded_semantic_stabilizers.to_dataframe()" ] }, { "cell_type": "markdown", "id": "4844962f", "metadata": {}, "source": [ "Now we calculate the $2n$ stabilizers of the Choi state encoding the $n$ qubit implementation of the logical operation. In this case, the physical Hadamard acts on seven qubits. Therefore we expect our implementation tableau to contain $2*7 =14$ Pauli stabilizers." ] }, { "cell_type": "code", "execution_count": 21, "id": "d63f3f4a-6530-49d1-a184-8b956b0ccaf2", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ComponentCoefficient
0X0 X3 X6 Z11 Z12 Z13+1
1Z0 Z3 Z6 X11 X12 X13+1
2X1 X3 X5 Z11 Z12 Z13+1
3Z1 Z3 Z5 X11 X12 X13+1
4X2 X3 X5 X6+1
5Z2 Z3 Z5 Z6+1
6X4 X5 X6 Z11 Z12 Z13+1
7Z4 Z5 Z6 X11 X12 X13+1
8X7 X10 X11 X12+1
9Z7 Z10 Z11 Z12+1
10X8 X10 X11 X13+1
11Z8 Z10 Z11 Z13+1
12X9 X10 X12 X13+1
13Z9 Z10 Z12 Z13+1
\n", "
" ], "text/plain": [ " Component Coefficient\n", "0 X0 X3 X6 Z11 Z12 Z13 +1\n", "1 Z0 Z3 Z6 X11 X12 X13 +1\n", "2 X1 X3 X5 Z11 Z12 Z13 +1\n", "3 Z1 Z3 Z5 X11 X12 X13 +1\n", "4 X2 X3 X5 X6 +1\n", "5 Z2 Z3 Z5 Z6 +1\n", "6 X4 X5 X6 Z11 Z12 Z13 +1\n", "7 Z4 Z5 Z6 X11 X12 X13 +1\n", "8 X7 X10 X11 X12 +1\n", "9 Z7 Z10 Z11 Z12 +1\n", "10 X8 X10 X11 X13 +1\n", "11 Z8 Z10 Z11 Z13 +1\n", "12 X9 X10 X12 X13 +1\n", "13 Z9 Z10 Z12 Z13 +1" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Calculate the 2n stabilizers of the Choi state encoding the physical operation.\n", "implementation_stabilizers = _compute_stabilizers_single_block_unitary(\n", " STEANE_DEF, steane_impl_h, num_selene_qubits=2 * (STEANE_DEF.n_physical_qubits)\n", ")\n", "implementation_stabilizers.to_dataframe()" ] }, { "cell_type": "markdown", "id": "751b4dd3", "metadata": {}, "source": [ "We now canonicalize these implementation stabilizers as we did above for the semantic stabilizers." ] }, { "cell_type": "code", "execution_count": 22, "id": "a44f6636-3ff6-48cf-873a-6961c75afcb8", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ComponentCoefficient
0X0 X3 X6 Z11 Z12 Z13+1
1X1 X3 X5 Z11 Z12 Z13+1
2X2 X3 X5 X6+1
3X4 X5 X6 Z11 Z12 Z13+1
4Z4 Z5 Z6 X7 X10 X13+1
5Z4 Z5 Z6 X8 X10 X12+1
6X9 X10 X12 X13+1
7Z4 Z5 Z6 X11 X12 X13+1
8Z0 Z3 Z4 Z5+1
9Z1 Z3 Z4 Z6+1
10Z2 Z3 Z5 Z6+1
11Z7 Z10 Z11 Z12+1
12Z8 Z10 Z11 Z13+1
13Z9 Z10 Z12 Z13+1
\n", "
" ], "text/plain": [ " Component Coefficient\n", "0 X0 X3 X6 Z11 Z12 Z13 +1\n", "1 X1 X3 X5 Z11 Z12 Z13 +1\n", "2 X2 X3 X5 X6 +1\n", "3 X4 X5 X6 Z11 Z12 Z13 +1\n", "4 Z4 Z5 Z6 X7 X10 X13 +1\n", "5 Z4 Z5 Z6 X8 X10 X12 +1\n", "6 X9 X10 X12 X13 +1\n", "7 Z4 Z5 Z6 X11 X12 X13 +1\n", "8 Z0 Z3 Z4 Z5 +1\n", "9 Z1 Z3 Z4 Z6 +1\n", "10 Z2 Z3 Z5 Z6 +1\n", "11 Z7 Z10 Z11 Z12 +1\n", "12 Z8 Z10 Z11 Z13 +1\n", "13 Z9 Z10 Z12 Z13 +1" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "implementation_stabilizers.canonicalize_all() # Canonicalize Clifford tableau\n", "implementation_stabilizers.to_dataframe()" ] }, { "cell_type": "markdown", "id": "3b6da479", "metadata": {}, "source": [ "Now that we have two Clifford tableaux in a suitable normal form, we can test them for equality." ] }, { "cell_type": "code", "execution_count": 23, "id": "96b716f1-5794-4947-a416-0b2652f2d30d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "expanded_semantic_stabilizers == implementation_stabilizers" ] }, { "cell_type": "markdown", "id": "04f51bd1-a562-4a5c-8bd6-0127313315f0", "metadata": {}, "source": [ "## Summary of available features\n", "\n", "The following features are available for verification of Clifford operations with {py:func}`~guppyft.verify.valid_clifford_implementation`\n", "\n", "* Works for $k>1$ codes\n", "* Works for operations on a single logical block or between two logical blocks\n", "* Works for non-CSS codes (e.g. the $[[5, 1, 3]]$ code)\n", "* Ancilla qubits can be used in the implementation\n", "\n", "The features above are also available in {py:func}`~guppyft.verify.valid_stabilizer_state_preparation` which can be used to verify the preparation of logical Pauli eigenstates (e.g. $|0\\rangle_L$, $|+\\rangle_L$)" ] } ], "metadata": { "kernelspec": { "display_name": "guppyft (3.14.0)", "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.12" } }, "nbformat": 4, "nbformat_minor": 5 }