{
"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",
"{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",
" Component | \n",
" Coefficient | \n",
"
\n",
" \n",
" \n",
" \n",
" | 0 | \n",
" X0 Z1 | \n",
" +1 | \n",
"
\n",
" \n",
" | 1 | \n",
" Z0 X1 | \n",
" +1 | \n",
"
\n",
" \n",
"
\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",
" Component | \n",
" Coefficient | \n",
"
\n",
" \n",
" \n",
" \n",
" | 0 | \n",
" X0 X1 X2 X3 X4 X5 X6 Z7 Z8 Z9 Z10 Z11 Z12 Z13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 1 | \n",
" Z0 Z1 Z2 Z3 Z4 Z5 Z6 X7 X8 X9 X10 X11 X12 X13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 2 | \n",
" X0 X1 X2 X3 | \n",
" +1 | \n",
"
\n",
" \n",
" | 3 | \n",
" X1 X2 X4 X5 | \n",
" +1 | \n",
"
\n",
" \n",
" | 4 | \n",
" X2 X3 X5 X6 | \n",
" +1 | \n",
"
\n",
" \n",
" | 5 | \n",
" Z0 Z1 Z2 Z3 | \n",
" +1 | \n",
"
\n",
" \n",
" | 6 | \n",
" Z1 Z2 Z4 Z5 | \n",
" +1 | \n",
"
\n",
" \n",
" | 7 | \n",
" Z2 Z3 Z5 Z6 | \n",
" +1 | \n",
"
\n",
" \n",
" | 8 | \n",
" X7 X8 X9 X10 | \n",
" +1 | \n",
"
\n",
" \n",
" | 9 | \n",
" X8 X9 X11 X12 | \n",
" +1 | \n",
"
\n",
" \n",
" | 10 | \n",
" X9 X10 X12 X13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 11 | \n",
" Z7 Z8 Z9 Z10 | \n",
" +1 | \n",
"
\n",
" \n",
" | 12 | \n",
" Z8 Z9 Z11 Z12 | \n",
" +1 | \n",
"
\n",
" \n",
" | 13 | \n",
" Z9 Z10 Z12 Z13 | \n",
" +1 | \n",
"
\n",
" \n",
"
\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",
" Component | \n",
" Coefficient | \n",
"
\n",
" \n",
" \n",
" \n",
" | 0 | \n",
" X0 X3 X6 Z11 Z12 Z13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 1 | \n",
" X1 X3 X5 Z11 Z12 Z13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 2 | \n",
" X2 X3 X5 X6 | \n",
" +1 | \n",
"
\n",
" \n",
" | 3 | \n",
" X4 X5 X6 Z11 Z12 Z13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 4 | \n",
" Z4 Z5 Z6 X7 X10 X13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 5 | \n",
" Z4 Z5 Z6 X8 X10 X12 | \n",
" +1 | \n",
"
\n",
" \n",
" | 6 | \n",
" X9 X10 X12 X13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 7 | \n",
" Z4 Z5 Z6 X11 X12 X13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 8 | \n",
" Z0 Z3 Z4 Z5 | \n",
" +1 | \n",
"
\n",
" \n",
" | 9 | \n",
" Z1 Z3 Z4 Z6 | \n",
" +1 | \n",
"
\n",
" \n",
" | 10 | \n",
" Z2 Z3 Z5 Z6 | \n",
" +1 | \n",
"
\n",
" \n",
" | 11 | \n",
" Z7 Z10 Z11 Z12 | \n",
" +1 | \n",
"
\n",
" \n",
" | 12 | \n",
" Z8 Z10 Z11 Z13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 13 | \n",
" Z9 Z10 Z12 Z13 | \n",
" +1 | \n",
"
\n",
" \n",
"
\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",
" Component | \n",
" Coefficient | \n",
"
\n",
" \n",
" \n",
" \n",
" | 0 | \n",
" X0 X3 X6 Z11 Z12 Z13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 1 | \n",
" Z0 Z3 Z6 X11 X12 X13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 2 | \n",
" X1 X3 X5 Z11 Z12 Z13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 3 | \n",
" Z1 Z3 Z5 X11 X12 X13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 4 | \n",
" X2 X3 X5 X6 | \n",
" +1 | \n",
"
\n",
" \n",
" | 5 | \n",
" Z2 Z3 Z5 Z6 | \n",
" +1 | \n",
"
\n",
" \n",
" | 6 | \n",
" X4 X5 X6 Z11 Z12 Z13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 7 | \n",
" Z4 Z5 Z6 X11 X12 X13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 8 | \n",
" X7 X10 X11 X12 | \n",
" +1 | \n",
"
\n",
" \n",
" | 9 | \n",
" Z7 Z10 Z11 Z12 | \n",
" +1 | \n",
"
\n",
" \n",
" | 10 | \n",
" X8 X10 X11 X13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 11 | \n",
" Z8 Z10 Z11 Z13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 12 | \n",
" X9 X10 X12 X13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 13 | \n",
" Z9 Z10 Z12 Z13 | \n",
" +1 | \n",
"
\n",
" \n",
"
\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",
" Component | \n",
" Coefficient | \n",
"
\n",
" \n",
" \n",
" \n",
" | 0 | \n",
" X0 X3 X6 Z11 Z12 Z13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 1 | \n",
" X1 X3 X5 Z11 Z12 Z13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 2 | \n",
" X2 X3 X5 X6 | \n",
" +1 | \n",
"
\n",
" \n",
" | 3 | \n",
" X4 X5 X6 Z11 Z12 Z13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 4 | \n",
" Z4 Z5 Z6 X7 X10 X13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 5 | \n",
" Z4 Z5 Z6 X8 X10 X12 | \n",
" +1 | \n",
"
\n",
" \n",
" | 6 | \n",
" X9 X10 X12 X13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 7 | \n",
" Z4 Z5 Z6 X11 X12 X13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 8 | \n",
" Z0 Z3 Z4 Z5 | \n",
" +1 | \n",
"
\n",
" \n",
" | 9 | \n",
" Z1 Z3 Z4 Z6 | \n",
" +1 | \n",
"
\n",
" \n",
" | 10 | \n",
" Z2 Z3 Z5 Z6 | \n",
" +1 | \n",
"
\n",
" \n",
" | 11 | \n",
" Z7 Z10 Z11 Z12 | \n",
" +1 | \n",
"
\n",
" \n",
" | 12 | \n",
" Z8 Z10 Z11 Z13 | \n",
" +1 | \n",
"
\n",
" \n",
" | 13 | \n",
" Z9 Z10 Z12 Z13 | \n",
" +1 | \n",
"
\n",
" \n",
"
\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
}