From 1a3705c43b47ec44eae90849d2de5106ee3a26d9 Mon Sep 17 00:00:00 2001 From: nate stemen Date: Wed, 4 Sep 2024 14:21:25 -0700 Subject: [PATCH] QSE docs cleanup (#2490) --- docs/source/guide/qse-1-intro.md | 40 ++++++++++-------------------- docs/source/guide/qse-3-options.md | 18 ++++++++------ docs/source/guide/qse-5-theory.md | 24 +++++++++++------- 3 files changed, 38 insertions(+), 44 deletions(-) diff --git a/docs/source/guide/qse-1-intro.md b/docs/source/guide/qse-1-intro.md index 0d0dc7e3e..ee38520b9 100644 --- a/docs/source/guide/qse-1-intro.md +++ b/docs/source/guide/qse-1-intro.md @@ -18,15 +18,16 @@ Here we show how to use QSE by means of a simple example. To use QSE, we call `qse.execute_with_qse()` with five “ingredients”: - A quantum circuit to prepare a state -- A quantum computer or noisy simulator to return a QuantumResult from +- A quantum computer or noisy simulator which returns a {class}`.QuantumResult` - An observable we wish to compute the error mitigated expectation value -- A list of Check Operators which can be stabilizers, symmetries or anything else. -- A Code Hamiltonian which defines the state with the least amount of errors +- A list of "check operators" which can be stabilizers, symmetries, or anything else. +- A "code Hamiltonian" which defines the state with the least amount of errors ## 1. Define a quantum circuit The quantum circuit can be specified as any quantum circuit supported by Mitiq. -In the next cell we define (as an example) a quantum circuit that prepares the logical 0 state in the [[5,1,3]] code. For simplicity, we use Gram Schmidt orthogonalization to form a unitary matrix that we use as a gate to prepare our state. +In the next cell we define (as an example) a quantum circuit that prepares the logical 0 state in the [[5,1,3]] code. +For simplicity, we use Gram Schmidt orthogonalization to form a unitary matrix that we use as a gate to prepare our state. ```{code-cell} ipython3 def prepare_logical_0_state_for_5_1_3_code(): @@ -104,13 +105,11 @@ def prepare_logical_0_state_for_5_1_3_code(): circuit.append(g(*qubits)) return circuit - -def demo_qse(): - circuit = prepare_logical_0_state_for_5_1_3_code() ``` ## 2. Define an executor -We define an [executor](executors.md) function which inputs a circuit and returns a `QuantumResult`. Here for sake of example we use a simulator that adds single-qubit depolarizing noise after each moment and returns the final density matrix. +We define an [executor](executors.md) function which inputs a circuit and returns a `QuantumResult`. +Here for sake of example we use a simulator that adds single-qubit depolarizing noise after each moment and returns the final density matrix. ```{code-cell} ipython3 from typing import List @@ -127,14 +126,11 @@ def execute_with_depolarized_noise(circuit: QPROGRAM) -> np.ndarray: noise_model_function=cirq.depolarize, noise_level=(0.01,), ) - -def demo_qse(): - circuit = prepare_logical_0_state_for_5_1_3_code() - executor = execute_with_depolarized_noise ``` ## 3. Observable -We define an observable in the code subspace: $O = ZZZZZ$. As an example, assume that we wish to compute the expectation value $Tr[\rho O]$ of the observable O. +We define an observable in the code subspace: $O = ZZZZZ$. +As an example, assume that we wish to compute the expectation value $\mathrm{tr}[\rho O]$ of the observable $O$. ```{code-cell} ipython3 def get_observable_in_code_space(observable: List[cirq.PauliString]): @@ -152,14 +148,12 @@ def get_observable_in_code_space(observable: List[cirq.PauliString]): observable_in_code_space *= 0.5 * Observable(FIVE_I, g) return observable_in_code_space -def demo_qse(): - circuit = prepare_logical_0_state_for_5_1_3_code() - executor = execute_with_depolarized_noise - observable = get_observable_in_code_space(PauliString("ZZZZZ")) ``` ## 4. Check Operators and Code Hamiltonian -We then assign the check operators as a list of pauli strings, and the code Hamiltonian as an Observable for the [[5,1,3]] code. The check operators of the [[5,1,3]] code are simply the expansion of the code’s 4 generators: $[XZZXI, IXZZX, XIXZZ, ZXIXZ]$ + +We then assign the check operators as a list of pauli strings, and the code Hamiltonian as an Observable for the [[5,1,3]] code. +The check operators of the [[5,1,3]] code are simply the expansion of the code’s 4 generators: $[XZZXI, IXZZX, XIXZZ, ZXIXZ]$ ```{code-cell} ipython3 def get_5_1_3_code_check_operators_and_code_hamiltonian() -> tuple: @@ -195,19 +189,11 @@ def get_5_1_3_code_check_operators_and_code_hamiltonian() -> tuple: ] Hc = Observable(*negative_Ms_as_pauliStrings) return Ms_as_pauliStrings, Hc - - -def demo_qse(): - circuit = prepare_logical_0_state_for_5_1_3_code() - executor = execute_with_depolarized_noise - observable = get_observable_in_code_space(PauliString("ZZZZZ")) - check_operators, code_hamiltonian = get_5_1_3_code_check_operators_and_code_hamiltonian() ``` - ## Run QSE -Now we can run QSE. We first compute the noiseless result then the noisy result to compare to the mitigated result from QSE. +With everything defined, we can now run QSE in full. ```{code-cell} ipython3 def demo_qse(): diff --git a/docs/source/guide/qse-3-options.md b/docs/source/guide/qse-3-options.md index 0f3b1bb77..5baec9348 100644 --- a/docs/source/guide/qse-3-options.md +++ b/docs/source/guide/qse-3-options.md @@ -11,20 +11,22 @@ kernelspec: name: python3 --- - # What additional options are available in QSE? -In addition to the necessary ingredients already discussed in [How do I use QSE?](qse-1-intro.md), there are a few additional options included in the implementation. +In addition to the necessary ingredients already discussed in [How do I use QSE?](qse-1-intro.md), there are a few additional options included in the implementation. ## Caching Pauli Strings to Expectation Values -```{warning} -The cache object is modified in place. -``` Specifically, in order to save runtime, the QSE implementation supports the use of a cache that maps pauli strings to their expectation values. This is taken as an additional parameter in the [`execute_with_qse`](https://mitiq.readthedocs.io/en/stable/apidoc.html#mitiq.qse.qse.execute_with_qse) function. -The inclusion of the cache significantly speeds up the runtime and avoids the need for re-computation of already computed values. -Furthermore, it is important to note that the cache gets modified in place, so the user can pass the same cache object to `execute_with_qse` and avoid regenerating the cache unnecessarily , e.g. if the noise model is the same from one execution to another. +```{warning} +The cache object is modified in place when passing it to `execute_with_qse`. +``` + +The inclusion of the cache significantly speeds up the runtime and avoids the need for re-computation of already computed values. +Furthermore, since the cache is modified in place, it can be reused as long as the noise model remains the same. ## Requirements for Check Operators -It is also important to note that when specifying the check (or excitation) operators for the execution, it is not necessary to specify the full exponential number of operators. As many or as few operators can be specified. The tradeoff is the fidelity of the projected state. +When specifying the check operators, it is **not** necessary to specify the full exponential number of operators. +As many or as few operators can be specified. +The tradeoff is the fidelity of the projected state. diff --git a/docs/source/guide/qse-5-theory.md b/docs/source/guide/qse-5-theory.md index a95196a6d..6940f4cc4 100644 --- a/docs/source/guide/qse-5-theory.md +++ b/docs/source/guide/qse-5-theory.md @@ -13,18 +13,24 @@ kernelspec: # What is the theory behind QSE? -We implement the subspace expansion method introduced by McClean et al {cite}`McClean_2020_NatComm` +We implement the subspace expansion method introduced by McClean et al {cite}`McClean_2020_NatComm`. -Subspace expansion as an error mitigation scheme uses a set of operators, called the check operators, to expand around the output state, then search this subspace for the state with the lowest error. When used in conjunction with a quantum code, a subset of the stabilizer group can be used to expand around the output state. If the full stabilizer group is used, subspace expansion is equivalent to projecting the final state of the computation onto the codeword subspace. +Subspace expansion as an error mitigation scheme uses a set of operators, called the check operators, to define a subspace surrounding the output state. +The protocol then involves searching this subspace for the state with the lowest error. -The check operators can also include other operators outside the stabilizers group. For instance, if the final state of the computation is known to have some symmetry, including the symmetry operator as a check operator is useful in correcting errors that don’t respect the symmetry. In our code implementation, we allow the user to input the set of check operators, which can be stabilizers, symmetries, or anything else. +```{tip} +When used in conjunction with a quantum code, a subset of the stabilizer group can be used to expand around the output state. +If the full stabilizer group is used, subspace expansion is equivalent to projecting the final state of the computation onto the codeword subspace. +``` -Let $| \Psi \rangle$ be the state we wish to prepare on the quantum computer and $M_i$ be the set of check operators such that $ M_i | \Psi \rangle = | \Psi \rangle$. Furthermore, let $\rho$ represent the density matrix of the actual final state prepared on the device. Due to errors, $\rho \neq | \Psi \rangle \langle \Psi |$. However, we would like to use our knowledge about the state $| \Psi \rangle$ encoded in the check operators to mitigate the errors in $\rho$. +The check operators can also include other operators outside the stabilizers group. For instance, if the final state of the computation is known to have some symmetry, including the symmetry operator as a check operator is useful in correcting errors that don’t respect the symmetry. In our code implementation, we allow the user to input the set of check operators, which can be stabilizers, symmetries, or anything else. -As mentioned, the main idea is to expand around $\rho$ using the check operators, and then search this subspace for the state with least amount of error. The state with the least amount of energy is defined as the state in the subspace that minimize $\mathrm H = - \sum_i M_i$. Thus the full procedure can be formulated as follows, -$\min_{{c_i}} \text{Tr}[\bar P_c \rho \bar P_c^\dagger \mathrm H ]$ subject to the constrain $\text{Tr}[\bar P_c \rho \bar P_c^\dagger] = 1$ with $\bar P_c = \sum c_i M_i$. +Let $| \Psi \rangle$ be the state we wish to prepare on the quantum computer and $M_i$ be the set of check operators such that $ M_i | \Psi \rangle = | \Psi \rangle$. Furthermore, let $\rho$ represent the density matrix of the actual final state prepared on the device. Due to errors, $\rho \neq | \Psi \rangle \langle \Psi |$. However, we would like to use our knowledge about the state $| \Psi \rangle$ encoded in the check operators to mitigate the errors in $\rho$. -As is turned out, this minimization problem can be mapped to the following generalized eigenvalue problem: -$H \boldsymbol c = \lambda_0 S \boldsymbol c$ where $\boldsymbol c_i = c_i$, $H_{ij} = \text{Tr}[M_i^\dagger H M_i \rho]$, and $S_{ij} = \text{Tr}[M_i^\dagger M_i \rho]$. The eigenvector $\boldsymbol c$ corresponding to the lowest $\lambda$ is the solution to the minimization problem. +As mentioned, the main idea is to expand around $\rho$ using the check operators, and then search this subspace for the state with least amount of error. The state with the least amount of energy is defined as the state in the subspace that minimizes $H = - \sum_i M_i$. Thus the full procedure can be formulated as follows, +$\min_{{c_i}} \mathrm{tr}[\bar P_c \rho \bar P_c^\dagger H ]$ subject to the constrain $\mathrm{tr}[\bar P_c \rho \bar P_c^\dagger] = 1$ with $\bar P_c = \sum c_i M_i$. -In practice, $H_{ij}$ and $S_{ij}$ can be measured on the quantum computer and the eigenvalue problem can be solved classically. Once $\boldsymbol c$ is obtained, we can construct $\bar P_c$ and use it to calculate any observable $A$ of interest using $\text{Tr}[ \bar P_c \rho \bar P_c^\dagger A ]$. +This minimization problem can be mapped to the following generalized eigenvalue problem: +$H \boldsymbol c = \lambda_0 S \boldsymbol c$ where $\boldsymbol c_i = c_i$, $H_{ij} = \mathrm{tr}[M_i^\dagger H M_i \rho]$, and $S_{ij} = \mathrm{tr}[M_i^\dagger M_i \rho]$. The eigenvector $\boldsymbol c$ corresponding to the lowest $\lambda$ is the solution to the minimization problem. + +In practice, $H_{ij}$ and $S_{ij}$ can be measured on the quantum computer and the eigenvalue problem can be solved classically. Once $\boldsymbol c$ is obtained, we can construct $\bar P_c$ and use it to calculate any observable $A$ of interest using $\mathrm{tr}[ \bar P_c \rho \bar P_c^\dagger A ]$.