Skip to content

Commit

Permalink
first round of edits
Browse files Browse the repository at this point in the history
  • Loading branch information
purva-thakre committed Sep 18, 2023
1 parent abd46b5 commit 905ca96
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 86 deletions.
70 changes: 33 additions & 37 deletions docs/source/guide/pt-1-intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,66 +40,62 @@ circuit = Circuit(
print(circuit)
```

Next we define a simple executor function which inputs a circuit, executes
the circuit on a noisy simulator, and returns the probability of the ground
state. See the [Executors](executors.md) section for more information on
how to define more advanced executors.
Next, we use introduce noise into the input circuit and then use
{func}`.pauli_twirl_circuit()` to tailor the noise in the noisy
circuit. It is worth noting that this technique skips defining an executor
as the end goal is to tailor the noise rather than estimate the expectation
value of some observable.



```{code-cell} ipython3
import numpy as np
from cirq import DensityMatrixSimulator, amplitude_damp
from cirq import amplitude_damp
from mitiq.interface import convert_to_mitiq
def execute(circuit, noise_level=0.1):
"""Returns Tr[ρ |0⟩⟨0|] where ρ is the state prepared by the circuit
executed with amplitude damping noise.
"""
# Replace with code based on your frontend and backend.
mitiq_circuit, _ = convert_to_mitiq(circuit)
noisy_circuit = mitiq_circuit.with_noise(amplitude_damp(gamma=noise_level))
rho = DensityMatrixSimulator().simulate(noisy_circuit).final_density_matrix
return rho[0, 0].real
```
noise_level = 0.1
mitiq_circuit, _ = convert_to_mitiq(circuit)
noisy_circuit = mitiq_circuit.with_noise(amplitude_damp(gamma=noise_level))
The [executor](executors.md) can be used to evaluate noisy (unmitigated)
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) :.3}")
```{admonition} Note:
PT is designed to transform the noise simulated in this example,
but it should not be expected to always be a positive effect in terms of
obtaining a better expectation value. Thus, as discussed previously,
it is more of a noise tailoring technique, designed
to be composed with other error mitigation techniques rather than an
error mitigation technique on its own.
```

## Apply PT
Pauli Twirling can be easily implemented with the function
{func}`.execute_with_pt()`.
{func}`.pauli_twirl_circuit()`. As an example, the original input circuit without
noise inserted into it is used here.

```{code-cell} ipython3
from mitiq import pt
mitigated_result = pt.execute_with_pt(
circuit=circuit,
executor=execute,
tailored_circuit_ideal = pt.pauli_twirl_circuit(
circuit = circuit,
num_circuits = 11,
)
```
The default option for prividing different twirled versions of the same input circuit
is set to 10. For the choice above, the twirled outputs are as shown below.

```{code-cell} ipython3
print(f"Error with mitigation (PT): {abs(ideal_value - mitigated_result) :.3}")
for i in range(len(tailored_circuit_ideal)):
print(f"Example Pauli Twirled Circuit {i}: \n {tailored_circuit_ideal[i]}, \n")
```

Here we observe that the application of PT reduces the estimation error when compared
to the unmitigated result.
Here, we observe that the application of the Pauli Twirling technique has kept the logic
of the input circuit unchanged while introducing a combination of gates to twilred outputs.

```{admonition} Note:
PT is designed to transform the noise simulated in this example,
but it should nnot be expected to always be a positive effect.
In this sense, it is more of a noise tailoring technique, designed
to be composed with other techniques rather than an error mitigation
technique in and of itself.
```
## Combine PT with ZNE

Use unitary folding, define an executor and then show the better expectation value.
+++

The section
[What additional options are available when using PT?](pt-3-options.md)
contains information on more advanced ways of applying PT with Mitiq.
contains information on more additional ways of applying PT with Mitiq.
6 changes: 2 additions & 4 deletions docs/source/guide/pt-2-use-case.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@ kernelspec:

## Advantages

Pauli Twirling is a technique devised to tailor noise towards Pauli channels.
Pauli Twirling is a technique devised to tailor noise towards Pauli channels. More details on the theory behind Pauli Twirling and how the noise is transformed are given in the section [What is the theory behind PT?](pt-5-theory.md).

More details on the theory of Pauli Twirling are given in the section [What is the theory behind PT?](pt-5-theory.md).

Pauli Twirling is agnostic to our knowledge on the type of noise, easy to implement, and useful to better understand and minimize the benchmarking vs performance gap.
Pauli Twirling is agnostic to our knowledge on the type of noise, easy to implement, and useful to better understand and minimize the benchmarking vs. performance gap.



Expand Down
45 changes: 44 additions & 1 deletion docs/source/guide/pt-3-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,47 @@ kernelspec:

# What additional options are available when using PT?

Currently Pauli Twirling is designed to have relatively few options, in part to make it readily composable with every other Mitiq technique. In the future, we expect a possibility of supporting additional operations as targets (beyond CZ and CNOT gates), with more customization on picking those targets. Stay tuned!
Currently Pauli Twirling is designed to have relatively few options, in part to make it readily composable with every other Mitiq technique. In the future, we expect a possibility of supporting additional operations as targets (beyond CZ and CNOT gates), with more customization on picking those targets. Stay tuned!

Although, if the goal is to only twirl the CNOT gates or CZ gates in the input circuit, additional options exist
through different functions.

- {func}`.twirl_CZ_gates` will only twirl the CZ gates in the input circuit.
- {func}`.twirl_CNOT_gates` will only twirl the CZ gates in the input circuit.

Both available options do not have a default number for the number of twirled circuits in the output. For simplicity's sake,
the following demonstrations limit the output circuits to just 1.

Going back to our example circuit,

```{code-cell} ipython3
from cirq import LineQubit, Circuit, CZ, CNOT
a, b, c, d = LineQubit.range(4)
circuit = Circuit(
CNOT.on(a, b),
CZ.on(b, c),
CNOT.on(c, d),
)
print(circuit)
```

```{code-cell} ipython3
from mitiq import pt
twirl_CZ_only = pt.twirl_CZ_gates(
circuit = circuit,
num_circuits = 1,
)
twirl_CNOT_only = pt.twirl_CNOT_gates(
circuit = circuit,
num_circuits = 1,
)
```

```{code-cell} ipython3
print(f"Limit Twirling to CZ Gates: \n {twirl_CZ_only}, \n")
print(f"Limit Twirling to CNOT Gates: \n {twirl_CNOT_only}, \n")
```
71 changes: 32 additions & 39 deletions docs/source/guide/pt-4-low-level.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,28 @@ Workflow of the PT technique in Mitiq, detailed in the [What happens when I use

- The user provides a `QPROGRAM`, (i.e. a quantum circuit defined via any of the supported [frontends](frontends-backends.md)).
- Mitiq modifies the input circuit with the insertion of PT gates on noisy operations.
- The modified circuit is executed via a user-defined [Executor](executors.md).
- The error mitigated expectation value is returned to the user.
- The output of the PT function consists of multiple circuits.

With respect to the workflows of other error-mitigation techniques (e.g. [ZNE](zne-4-low-level.md) or [PEC](pec-4-low-level.md)),
PT involves the generation of a _single_ circuit with random modifications, and subsequently averages over many executions.
For this reason, there is no need for a complex final inference step, which is necessary for other
techniques, and so this average is instead trivial for PT.
For this reason, there is no need for a complex final inference step (unlike in other
techniques), and so this average is instead trivial for PT.

```{note}
When setting the `num_trials` option to a value larger than one, multiple circuits are actually generated by Mitiq and
the associated results are averaged to obtain the final expectation value. This more general case is not shown in the figure since
it can be considered as an average of independent single-circuit workflows.
```

As shown in [How do I use PT?](pt-1-intro.md), the function {func}`.execute_with_pt()` applies PT behind the scenes
As shown in [How do I use PT?](pt-1-intro.md), the function {func}`.pauli_twirl_circuit()` applies PT behind the scenes
and directly returns the error-mitigated expectation value.
In the next sections instead, we show how one can apply PT at a lower level, i.e., by:

- Twirling CZ and CNOT gates in the circuit;
- Executing the modified circuit.
- Estimate expectation value by averaging over randomized twirling circuits

## Twirling CZ and CNOT gates in the circuit
## Twirling the Input Circuit
To twirl about particular gates, we need the Pauli group for those gates. These groups are stored as lookup tables, in {attr}`mitiq.pt.pt.CNOT_twirling_gates` and {attr}`mitiq.pt.pt.CZ_twirling_gates`, so that we can randomly select a tuple from the group. Now we're ready to twirl our gates.

First let's define our circuit:
Expand Down Expand Up @@ -75,52 +74,46 @@ twirled_circuits = [
print(CNOT_twirled_circuits[0:1])
print(twirled_circuits[0:1])
```
We see that we return lists of the randomly twirled circuits, and so we must take a simple average over their expectation values.

## Executing the modified circuits
Now that we have our twirled circuits, let's simulate some noise and execute those circuits, using the {class}`mitiq.Executor` to collect the results.
```{code-cell} ipython3
from cirq import DensityMatrixSimulator, amplitude_damp
from mitiq import Executor


def execute(circuit, noise_level=0.003):
"""Returns Tr[ρ |00..⟩⟨00..|] where ρ is the state prepared by the circuit
executed with depolarizing noise.
"""
noisy_circuit = circuit.with_noise(amplitude_damp(noise_level))
rho = DensityMatrixSimulator().simulate(noisy_circuit).final_density_matrix
return rho[0, 0].real

executor = Executor(execute)
expvals = executor.evaluate(twirled_circuits, None)
```
## Verifying Pauli Twirling Works

It is worth checking if the technique does tailor the noise as expected. For this
demonstration, we compare the off-diagonal terms in the Pauli Transfer Matrix (PTM)
{cite}`Hashim_2021_PRX` of the original input circuit, the noisy input circuit as well
as the averaged PTM of the different possible Pauli twirled circuits. A detailed discussion
on the PTM is available in [theory section of PT](pt-5-theory.md).

## Estimate expectation value by averaging over randomized twirling circuits
Pauli Twirling doesn't require running the circuit at different noise levels or with different noise models. It applies a randomized sequence of Pauli operations within the same quantum circuit and averages the results to reduce the effect of the noise.
Now, instead of using the ideal input circuit, we use PT on the noisy circuit with the
default option for the number of twirled circuits.

```{code-cell} ipython3
import numpy as np
from typing import cast
from mitiq import pt
from cirq import amplitude_damp
from mitiq.interface import convert_to_mitiq
average = cast(float, np.average(expvals))
print(average)
noise_level = 0.1
mitiq_circuit, _ = convert_to_mitiq(circuit)
noisy_circuit = mitiq_circuit.with_noise(amplitude_damp(gamma=noise_level))
tailored_circuit_noisy = pt.pauli_twirl_circuit(
circuit = noisy_circuit
)
```
The PTM of the ideal circuit is in the following code block.

The PTM of the noisy circuit is in the following code block.

The PTM of the output from PTM is in the following code block.

Keep in mind, ths code is for illustration and that the noise level, type of noise (here amplitude damping), and the observable need to be adapted to the specific experiment.

If executed on a noiseless backend, a given `circuit_with_pt` and `circuit` are equivalent.

On a real backend, they have a different sensitivity to noise. The core idea of the PT technique is that,
`circuits_with_pt` (hopefully) tailors the noise into stochastic Pauli channels, such that a simple average over results
will return a mitigated result.
{func}`.pauli_twirl_circuit()` (hopefully) tailors the noise into stochastic Pauli channels, such that a simple average over results with an additional mitigation technique will return a mitigated result.

As a final remark, we stress that the low-level procedure that we have shown is exactly what {func}`.execute_with_pt()` does behind the scenes.
Let's verify this fact:
## Verifying Pauli Twirling Works With Another Error Mitigation Technique

```{code-cell} ipython3
np.isclose(
pt.execute_with_pt(circuit, executor),
average,
)
```
Combine PT with Unitary Folding. Keep the example here consistent with the example in the intro.
8 changes: 8 additions & 0 deletions docs/source/guide/pt-5-theory.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,11 @@ With a single circuit, both the computational cost and complexity are reduced, m

- As a consequence of the previous point, the fundamental error mitigation overhead is minimized,
such that there is no increase in statistical uncertainty in the final result, assuming optimal executions.

## What are simpler stochastic Pauli noise channels?

Discuss (in detail) how the noise is beign transformed.

## Pauli Transfer Matrix (PTM)

Add info about the off-diagnoal terms in PTM - ideal, with noise, and after twirling.
12 changes: 7 additions & 5 deletions docs/source/guide/pt.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# Pauli Twirling

Pauli Twirling (PT) is an noise tailoring technique in which,
in the Mitiq implementation, particularly noisy operations (e.g. CZ and CNOT)
are transformed by independent, random, single-qubit gates inserted into
the circuit such that the effective logical circuit remains unchanged
but the noise is tailored towards stochastic Pauli errors.
Pauli Twirling (PT) is a noise tailoring technique in Mitiq, which transforms
noisy two-qubit operations (e.g. CZ and CNOT) such that the effective logical
circuit remains unchanged but the noise is tailored towards stochastic Pauli
errors. This transformation is performed by inserting independent, randomly
selected, single-qubit into the input circuit while ensuring the logic of the
input circuit remains unchnaged.

For more discussion of the theory of PT, see the section [What is the theory
behind PT?](pt-5-theory.md).

Expand Down

0 comments on commit 905ca96

Please sign in to comment.