Skip to content

Commit

Permalink
Add intro section to LRE docs (#2535)
Browse files Browse the repository at this point in the history
* add intro and use case pages

Co-Authored-By: Purva Thakre <[email protected]>

* clean up intro/use case

* clarify depth comment

* wordsmithing

---------

Co-authored-by: Purva Thakre <[email protected]>
  • Loading branch information
natestemen and Purva Thakre authored Oct 11, 2024
1 parent e31558f commit 2b45cc9
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 3 deletions.
152 changes: 152 additions & 0 deletions docs/source/guide/lre-1-intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
---
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 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.

```{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 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 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
from mitiq import benchmarks
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 measuring the ground state.
By altering the value for `noise_level`, ideal and noisy expectation values can be obtained.

```{code-cell} ipython3
from cirq import DensityMatrixSimulator, depolarize
def execute(circuit, noise_level=0.025):
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
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
degree = 2
fold_multiplier = 3
mitigated = execute_with_lre(
circuit,
execute,
degree=degree,
fold_multiplier=fold_multiplier,
)
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 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 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, degree, fold_multiplier)
num_scaled_circuits = 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}"
)
```

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
noise_scaled_circuits[3]
```

With the many noise-scaled circuits in hand, we can run them through our executor to obtain the expectation values.

```{code-cell} ipython3
noise_scaled_exp_values = [
execute(circuit) for circuit in noise_scaled_circuits
]
```

### 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
coefficients = multivariate_richardson_coefficients(
circuit,
fold_multiplier=fold_multiplier,
degree=degree,
)
```

Each noise scaled circuit has a coefficient of linear combination and a noisy expectation value associated with it.

### Combine the results

```{code-cell} ipython3
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}}"
)
```

As you can see we again see a nice improvement in the accuracy using a two stage application of LRE.
35 changes: 35 additions & 0 deletions docs/source/guide/lre-2-use-case.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
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

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 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 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.

```{attention}
We are currently investigating the issue related to the performance of chunking large circuits.
```
6 changes: 3 additions & 3 deletions docs/source/guide/lre.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

```{warning}
The user guide for LRE in Mitiq is currently under construction.
```
Expand All @@ -16,13 +15,14 @@ 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:

```{toctree}
---
maxdepth: 1
---
lre-1-intro.md
lre-2-use-case.md
lre-5-theory.md
```
```

0 comments on commit 2b45cc9

Please sign in to comment.