From 114eab1f56b91c279305a9f68c445051c58250b1 Mon Sep 17 00:00:00 2001 From: nate stemen Date: Thu, 10 Oct 2024 14:56:14 -0700 Subject: [PATCH 1/4] add intro and use case pages Co-Authored-By: Purva Thakre --- docs/source/guide/lre-1-intro.md | 185 ++++++++++++++++++++++++++++ docs/source/guide/lre-2-use-case.md | 46 +++++++ docs/source/guide/lre.md | 6 +- 3 files changed, 234 insertions(+), 3 deletions(-) create mode 100644 docs/source/guide/lre-1-intro.md create mode 100644 docs/source/guide/lre-2-use-case.md diff --git a/docs/source/guide/lre-1-intro.md b/docs/source/guide/lre-1-intro.md new file mode 100644 index 000000000..baaa4bb80 --- /dev/null +++ b/docs/source/guide/lre-1-intro.md @@ -0,0 +1,185 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst + format_version: 0.13 + jupytext_version: 1.11.1 +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + + +# How do I use LRE? + + +LRE works in two steps: generate noise-scaled circuits and apply inference to results from executed circuits. + + +A user has the choice to either use {func}`.execute_with_lre` to combine both steps into one if they are +only interested in obtaining the mitigated expectation value or splitting the process into two using +{func}`.multivariate_layer_scaling` and {func}`.multivariate_richardson_coefficients`. + + +```{warning} +LRE is currently compatible with quantum programs written using `cirq`. Work on making this technique compatible with other frontends is ongoing. 🚧 +``` + + +## Problem Setup + + +To use {func}`.execute_with_lre` without any additional options the following are required: + + +- a quantum circuit +- a method of returning an expectation value from a circuit +- the degree of the multivariate polynomial extrapolation +- fold multiplier AKA the scaling gap which is used to generate the scale factor vectors + + +### Define the circuit of interest + + +For simplicity, we define a simple circuit whose ideal execution is identical to the identity operation. + + +```{code-cell} ipython3 +from mitiq import benchmarks + + +circuit = benchmarks.generate_rb_circuits(n_qubits=1, num_cliffords=3)[0] + + +print(circuit) +``` + + +### Define executor for ideal and noisy executions + + +We define an [executor](executors.md) which executes the input circuit subjected to depolarizing noise, and returns the probability of the ground state. By altering the value for `noise_level`, ideal and noisy expectation +values can be obtained. + + +```{code-cell} ipython3 +import numpy as np +from cirq import DensityMatrixSimulator, depolarize + + +def execute(circuit, noise_level=0.025): + """Default executor for all unit tests.""" + noisy_circuit = circuit.with_noise(depolarize(p=noise_level)) + rho = DensityMatrixSimulator().simulate(noisy_circuit).final_density_matrix + return rho[0, 0].real +``` + + +Compare the noisy and ideal expectation values: + + +```{code-cell} ipython3 +# Compute the expectation value of the |0><0| observable. +noisy_value = execute(circuit) +ideal_value = execute(circuit, noise_level=0.0) +print(f"Error without mitigation: {abs(ideal_value - noisy_value) :.5f}") +``` + + +## Apply LRE directly + + +With the circuit, and executor defined, we just need to choose the polynomial extrapolation degree as well as the fold multiplier. + + +```{code-cell} ipython3 +from mitiq.lre import execute_with_lre + + +input_degree = 2 +input_fold_multiplier = 3 + + +mitigated_result = execute_with_lre( + circuit, + execute, + degree = input_degree, + fold_multiplier = input_fold_multiplier, +) + + +print(f"Error with mitigation (ZNE): {abs(ideal_value - mitigated_result):.{3}}") +``` + + +## Step by step application of LRE + +In this section we will walk through what happens in each of the two stages of LRE. + + +### Create noise-scaled circuits + + +We start with creating a number of noise-scaled circuits which we will pass to the executor. + + +```{code-cell} ipython3 +from mitiq.lre import multivariate_layer_scaling + + +noise_scaled_circuits = multivariate_layer_scaling(circuit, input_degree, input_fold_multiplier) + + +print(f"total number of noise-scaled circuits for LRE = {len(noise_scaled_circuits)}") +``` +An example noise-scaled circuit is shown below: + + +```{code-cell} ipython3 +noise_scaled_circuits[3] +``` + + +### Classical inference + + +Based on the choice of input parameters, a sample matrix created using {func}`.sample_matrix` is used to find the +coefficients of linear combination required for multivariate Richardson extrapolation (**link theory section here**). + + +```{code-cell} ipython3 +from mitiq.lre import multivariate_richardson_coefficients + +coeffs_of_linear_comb = multivariate_richardson_coefficients( + circuit, + fold_multiplier = input_fold_multiplier, + degree = input_degree, +) + + +print(f"total number of noise-scaled circuits for LRE = {len(noise_scaled_circuits)}") +print(f"total number of coefficients of linear combination for LRE = {len(coeffs_of_linear_comb)}") +``` +Each noise scaled circuit has a coefficient of linear combination and a noisy expectation value associated with it. + + +### Combine the results + + +```{code-cell} ipython3 +## execute each noise scaled circuit + +noise_scaled_exp_values = [] + + +for i in noise_scaled_circuits: + noise_scaled_exp_values.append(execute(i)) + + +calculated_mitigated_result = np.dot(noise_scaled_exp_values, coeffs_of_linear_comb) +print(f"Error with mitigation (ZNE): {abs(ideal_value - calculated_mitigated_result):.{3}}") +``` +The section [](lre-3-options.md) contains more information on other options available in LRE in addition to how to +control the hyperparameters associated with the LRE options. diff --git a/docs/source/guide/lre-2-use-case.md b/docs/source/guide/lre-2-use-case.md new file mode 100644 index 000000000..be7b82bd4 --- /dev/null +++ b/docs/source/guide/lre-2-use-case.md @@ -0,0 +1,46 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst + format_version: 0.13 + jupytext_version: 1.10.3 +kernelspec: + display_name: Python 3 (ipykernel) + language: python + name: python3 +--- + + +# When should I use LRE? + + +## Advantages + + +Layerwise Richardson Extrapolation is a generalized multivariate extension of the Richardson extrapolation where the univariate +version is available as an option in [ZNE](zne-3-options.md). Just as in ZNE, LRE can also be applied without a detailed knowledge of the underlying noise model as the effectiveness of the technique depends on the choice of scale factors. Thus, LRE is useful in scenarios where tomography is impractical. + + +The sampling overhead is flexible wherein the cost can be reduced by using larger values for the fold multiplier (used to +create the noise-scaled circuits) or by chunking a larger circuit into a smaller number of chunks. + + + + +## Disadvantages + + +When using a large circuit, the number of noise scaled circuits grows polynomially such that the execution time rises because we require the sample matrix to be a square matrix (**link theory page here**). + + +If one aims to reduce the sampling cost by using a larger fold multiplier, the bias for polynomial extrapolation increases as one moves farther away from the zero-noise limit. + + +Chunking a large circuit with a lower number of chunks to reduce the sampling cost can reduce the performance of LRE. In ZNE parlance, this is equivalent to local folding faring better than global folding in LRE when we use a higher number of chunks in [LRE](lre-3-options.md). + + +```{note} +We are currently investigating the issue related to chunking large circuits, as reduced performance has been noticed in our testing. +``` + diff --git a/docs/source/guide/lre.md b/docs/source/guide/lre.md index 410bef69c..cf02d2892 100644 --- a/docs/source/guide/lre.md +++ b/docs/source/guide/lre.md @@ -1,4 +1,3 @@ - ```{warning} The user guide for LRE in Mitiq is currently under construction. ``` @@ -11,7 +10,6 @@ circuit such that the noiseless expectation value is extrapolated from the execu noisy circuit (see the section [What is the theory behind LRE?](lre-5-theory.md)). Compared to Zero-Noise Extrapolation, this technique treats the noise in each layer of the circuit as an independent variable to be scaled and then extrapolated independently. - You can get started with LRE in Mitiq with the following sections of the user guide: @@ -19,5 +17,7 @@ You can get started with LRE in Mitiq with the following sections of the user gu --- maxdepth: 1 --- +lre-1-intro.md +lre-2-use-case.md lre-5-theory.md -``` \ No newline at end of file +``` From ce376f935d308b505136cdc8b0ccee36226bae93 Mon Sep 17 00:00:00 2001 From: nate stemen Date: Thu, 10 Oct 2024 17:19:25 -0700 Subject: [PATCH 2/4] clean up intro/use case --- docs/source/guide/lre-1-intro.md | 161 +++++++++++----------------- docs/source/guide/lre-2-use-case.md | 45 +++----- 2 files changed, 81 insertions(+), 125 deletions(-) diff --git a/docs/source/guide/lre-1-intro.md b/docs/source/guide/lre-1-intro.md index baaa4bb80..50e48d761 100644 --- a/docs/source/guide/lre-1-intro.md +++ b/docs/source/guide/lre-1-intro.md @@ -1,50 +1,34 @@ --- jupytext: - text_representation: - extension: .md - format_name: myst - format_version: 0.13 - jupytext_version: 1.11.1 + text_representation: + extension: .md + format_name: myst + format_version: 0.13 + jupytext_version: 1.11.1 kernelspec: - display_name: Python 3 - language: python - name: python3 + display_name: Python 3 + language: python + name: python3 --- - # How do I use LRE? +LRE works in two main steps: generate noise-scaled circuits, and apply inference to results from executed circuits. -LRE works in two steps: generate noise-scaled circuits and apply inference to results from executed circuits. - - -A user has the choice to either use {func}`.execute_with_lre` to combine both steps into one if they are -only interested in obtaining the mitigated expectation value or splitting the process into two using -{func}`.multivariate_layer_scaling` and {func}`.multivariate_richardson_coefficients`. +This workflow can be executed by a single call to {func}`.execute_with_lre`. +If more control is needed over the protocol, Mitiq provides {func}`.multivariate_layer_scaling` and {func}`.multivariate_richardson_coefficients` to handle the first and second steps respectively. - -```{warning} -LRE is currently compatible with quantum programs written using `cirq`. Work on making this technique compatible with other frontends is ongoing. 🚧 +```{danger} +LRE is currently compatible with quantum programs written using `cirq`. +Work on making this technique compatible with other frontends is ongoing. 🚧 ``` - ## Problem Setup - -To use {func}`.execute_with_lre` without any additional options the following are required: - - -- a quantum circuit -- a method of returning an expectation value from a circuit -- the degree of the multivariate polynomial extrapolation -- fold multiplier AKA the scaling gap which is used to generate the scale factor vectors - - -### Define the circuit of interest - +To demonstrate the use of LRE, we'll first define a quantum circuit, and a method of executing circuits for demonstration purposes. For simplicity, we define a simple circuit whose ideal execution is identical to the identity operation. - +Here we will use a randomized benchmarking circuit on a single qubit, visualized below. ```{code-cell} ipython3 from mitiq import benchmarks @@ -52,134 +36,117 @@ from mitiq import benchmarks circuit = benchmarks.generate_rb_circuits(n_qubits=1, num_cliffords=3)[0] - print(circuit) ``` - -### Define executor for ideal and noisy executions - - -We define an [executor](executors.md) which executes the input circuit subjected to depolarizing noise, and returns the probability of the ground state. By altering the value for `noise_level`, ideal and noisy expectation -values can be obtained. - +We define an [executor](executors.md) which simulates the input circuit subjected to depolarizing noise, and returns the probability of the ground state. +By altering the value for `noise_level`, ideal and noisy expectation values can be obtained. ```{code-cell} ipython3 -import numpy as np from cirq import DensityMatrixSimulator, depolarize def execute(circuit, noise_level=0.025): - """Default executor for all unit tests.""" - noisy_circuit = circuit.with_noise(depolarize(p=noise_level)) - rho = DensityMatrixSimulator().simulate(noisy_circuit).final_density_matrix - return rho[0, 0].real + noisy_circuit = circuit.with_noise(depolarize(p=noise_level)) + rho = DensityMatrixSimulator().simulate(noisy_circuit).final_density_matrix + return rho[0, 0].real ``` - Compare the noisy and ideal expectation values: - ```{code-cell} ipython3 -# Compute the expectation value of the |0><0| observable. -noisy_value = execute(circuit) -ideal_value = execute(circuit, noise_level=0.0) -print(f"Error without mitigation: {abs(ideal_value - noisy_value) :.5f}") +noisy = execute(circuit) +ideal = execute(circuit, noise_level=0.0) +print(f"Error without mitigation: {abs(ideal - noisy) :.5f}") ``` - ## Apply LRE directly - With the circuit, and executor defined, we just need to choose the polynomial extrapolation degree as well as the fold multiplier. - ```{code-cell} ipython3 from mitiq.lre import execute_with_lre -input_degree = 2 -input_fold_multiplier = 3 - +degree = 2 +fold_multiplier = 3 -mitigated_result = execute_with_lre( +mitigated = execute_with_lre( circuit, execute, - degree = input_degree, - fold_multiplier = input_fold_multiplier, + degree=degree, + fold_multiplier=fold_multiplier, ) - -print(f"Error with mitigation (ZNE): {abs(ideal_value - mitigated_result):.{3}}") +print(f"Error with mitigation (LRE): {abs(ideal - mitigated):.{3}}") ``` +As you can see, the technique is extremely simple to apply, and no knowledge of the hardware/simulator noise is required. ## Step by step application of LRE -In this section we will walk through what happens in each of the two stages of LRE. - +In this section we demonstrate the use of {func}`.multivariate_layer_scaling` and {func}`.multivariate_richardson_coefficients` for those who might want to inspect the intermediary circuits, and have more control over the protocol. ### Create noise-scaled circuits - -We start with creating a number of noise-scaled circuits which we will pass to the executor. - +We start by creating a number of noise-scaled circuits which we will pass to the executor. ```{code-cell} ipython3 from mitiq.lre import multivariate_layer_scaling -noise_scaled_circuits = multivariate_layer_scaling(circuit, input_degree, input_fold_multiplier) +noise_scaled_circuits = multivariate_layer_scaling(circuit, degree, fold_multiplier) +num_scaled_circuits = len(noise_scaled_circuits) - -print(f"total number of noise-scaled circuits for LRE = {len(noise_scaled_circuits)}") +print(f"total number of noise-scaled circuits for LRE = {num_scaled_circuits}") +print( + f"Average circuit depth = {sum(len(circuit) for circuit in noise_scaled_circuits) / num_scaled_circuits}" +) ``` -An example noise-scaled circuit is shown below: +Even though though our circuit of interest has depth 8, the noise scaled circuits are on average much longer. +An example noise-scaled circuit is shown below. ```{code-cell} ipython3 noise_scaled_circuits[3] ``` +With the many noise-scaled circuits in hand, we can run them through our executor to obtain the expectation values. -### Classical inference - +```{code-cell} ipython3 +noise_scaled_exp_values = [ + execute(circuit) for circuit in noise_scaled_circuits +] +``` -Based on the choice of input parameters, a sample matrix created using {func}`.sample_matrix` is used to find the -coefficients of linear combination required for multivariate Richardson extrapolation (**link theory section here**). +### Classical inference +The penultimate step here is to fetch the coefficients we'll use to combine the noisy data we obtained above. +The astute reader will note that we haven't defined or used a `degree` or `fold_multiplier` parameter, and this is where they are both needed. ```{code-cell} ipython3 from mitiq.lre import multivariate_richardson_coefficients -coeffs_of_linear_comb = multivariate_richardson_coefficients( + +coefficients = multivariate_richardson_coefficients( circuit, - fold_multiplier = input_fold_multiplier, - degree = input_degree, + fold_multiplier=fold_multiplier, + degree=degree, ) - - -print(f"total number of noise-scaled circuits for LRE = {len(noise_scaled_circuits)}") -print(f"total number of coefficients of linear combination for LRE = {len(coeffs_of_linear_comb)}") ``` -Each noise scaled circuit has a coefficient of linear combination and a noisy expectation value associated with it. +Each noise scaled circuit has a coefficient of linear combination and a noisy expectation value associated with it. ### Combine the results - ```{code-cell} ipython3 -## execute each noise scaled circuit - -noise_scaled_exp_values = [] - - -for i in noise_scaled_circuits: - noise_scaled_exp_values.append(execute(i)) - - -calculated_mitigated_result = np.dot(noise_scaled_exp_values, coeffs_of_linear_comb) -print(f"Error with mitigation (ZNE): {abs(ideal_value - calculated_mitigated_result):.{3}}") +mitigated = sum( + exp_val * coeff + for exp_val, coeff in zip(noise_scaled_exp_values, coefficients) +) +print( + f"Error with mitigation (LRE): {abs(ideal - mitigated):.{3}}" +) ``` -The section [](lre-3-options.md) contains more information on other options available in LRE in addition to how to -control the hyperparameters associated with the LRE options. + +As you can see we again see a nice improvement in the accuracy using a two stage application of LRE. diff --git a/docs/source/guide/lre-2-use-case.md b/docs/source/guide/lre-2-use-case.md index be7b82bd4..12b1d1395 100644 --- a/docs/source/guide/lre-2-use-case.md +++ b/docs/source/guide/lre-2-use-case.md @@ -1,46 +1,35 @@ --- jupytext: - text_representation: - extension: .md - format_name: myst - format_version: 0.13 - jupytext_version: 1.10.3 + text_representation: + extension: .md + format_name: myst + format_version: 0.13 + jupytext_version: 1.10.3 kernelspec: - display_name: Python 3 (ipykernel) - language: python - name: python3 + display_name: Python 3 (ipykernel) + language: python + name: python3 --- - # When should I use LRE? - ## Advantages - -Layerwise Richardson Extrapolation is a generalized multivariate extension of the Richardson extrapolation where the univariate -version is available as an option in [ZNE](zne-3-options.md). Just as in ZNE, LRE can also be applied without a detailed knowledge of the underlying noise model as the effectiveness of the technique depends on the choice of scale factors. Thus, LRE is useful in scenarios where tomography is impractical. - +Just as in ZNE, LRE can also be applied without a detailed knowledge of the underlying noise model as the effectiveness of the technique depends on the choice of scale factors. +Thus, LRE is useful in scenarios where tomography is impractical. The sampling overhead is flexible wherein the cost can be reduced by using larger values for the fold multiplier (used to -create the noise-scaled circuits) or by chunking a larger circuit into a smaller number of chunks. - - - +create the noise-scaled circuits) or by chunking a larger circuit to fold groups of layers of circuits instead of each one individually. ## Disadvantages +When using a large circuit, the number of noise scaled circuits grows polynomially such that the execution time rises because we require the sample matrix to be a square matrix (more details in the [theory](lre-5-theory.md) section). -When using a large circuit, the number of noise scaled circuits grows polynomially such that the execution time rises because we require the sample matrix to be a square matrix (**link theory page here**). - - -If one aims to reduce the sampling cost by using a larger fold multiplier, the bias for polynomial extrapolation increases as one moves farther away from the zero-noise limit. +When reducing the sampling cost by using a larger fold multiplier, the bias for polynomial extrapolation increases as one moves farther away from the zero-noise limit. +Chunking a large circuit with a lower number of chunks to reduce the sampling cost can reduce the performance of LRE. +In ZNE parlance, this is equivalent to local folding faring better than global folding in LRE when we use a higher number of chunks in LRE. -Chunking a large circuit with a lower number of chunks to reduce the sampling cost can reduce the performance of LRE. In ZNE parlance, this is equivalent to local folding faring better than global folding in LRE when we use a higher number of chunks in [LRE](lre-3-options.md). - - -```{note} -We are currently investigating the issue related to chunking large circuits, as reduced performance has been noticed in our testing. +```{attention} +We are currently investigating the issue related to the performance of chunking large circuits. ``` - From ea14e692b5b93250de7ca9b062e8c6e56726b624 Mon Sep 17 00:00:00 2001 From: nate stemen Date: Thu, 10 Oct 2024 17:54:30 -0700 Subject: [PATCH 3/4] clarify depth comment --- docs/source/guide/lre-1-intro.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/guide/lre-1-intro.md b/docs/source/guide/lre-1-intro.md index 50e48d761..a83aecba2 100644 --- a/docs/source/guide/lre-1-intro.md +++ b/docs/source/guide/lre-1-intro.md @@ -104,7 +104,7 @@ print( ) ``` -Even though though our circuit of interest has depth 8, the noise scaled circuits are on average much longer. +As you can see, the noise scaled circuits are on average much longer than the original circuit. An example noise-scaled circuit is shown below. ```{code-cell} ipython3 From f2f38290f13995f61aa2b555ea47ad94f3845eac Mon Sep 17 00:00:00 2001 From: nate stemen Date: Thu, 10 Oct 2024 17:59:34 -0700 Subject: [PATCH 4/4] wordsmithing --- docs/source/guide/lre-1-intro.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/guide/lre-1-intro.md b/docs/source/guide/lre-1-intro.md index a83aecba2..660483730 100644 --- a/docs/source/guide/lre-1-intro.md +++ b/docs/source/guide/lre-1-intro.md @@ -13,7 +13,7 @@ kernelspec: # How do I use LRE? -LRE works in two main steps: generate noise-scaled circuits, and apply inference to results from executed circuits. +LRE works in two main stages: generate noise-scaled circuits via layerwise scaling, and apply inference to resulting measurements post-execution. This workflow can be executed by a single call to {func}`.execute_with_lre`. If more control is needed over the protocol, Mitiq provides {func}`.multivariate_layer_scaling` and {func}`.multivariate_richardson_coefficients` to handle the first and second steps respectively. @@ -27,7 +27,7 @@ Work on making this technique compatible with other frontends is ongoing. 🚧 To demonstrate the use of LRE, we'll first define a quantum circuit, and a method of executing circuits for demonstration purposes. -For simplicity, we define a simple circuit whose ideal execution is identical to the identity operation. +For simplicity, we define a circuit whose unitary compiles to the identity operation. Here we will use a randomized benchmarking circuit on a single qubit, visualized below. ```{code-cell} ipython3 @@ -39,7 +39,7 @@ circuit = benchmarks.generate_rb_circuits(n_qubits=1, num_cliffords=3)[0] print(circuit) ``` -We define an [executor](executors.md) which simulates the input circuit subjected to depolarizing noise, and returns the probability of the ground state. +We define an [executor](executors.md) which simulates the input circuit subjected to depolarizing noise, and returns the probability of measuring the ground state. By altering the value for `noise_level`, ideal and noisy expectation values can be obtained. ```{code-cell} ipython3 @@ -62,7 +62,7 @@ print(f"Error without mitigation: {abs(ideal - noisy) :.5f}") ## Apply LRE directly -With the circuit, and executor defined, we just need to choose the polynomial extrapolation degree as well as the fold multiplier. +With the circuit and executor defined, we just need to choose the polynomial extrapolation degree as well as the fold multiplier. ```{code-cell} ipython3 from mitiq.lre import execute_with_lre