unitaryfund / mitiq

Mitiq is an open source toolkit for implementing error mitigation techniques on most current intermediate-scale quantum computers.
https://mitiq.readthedocs.io
GNU General Public License v3.0
363 stars 160 forks source link

Ensure `mitiq.pt` works effectively on simulators #2201

Closed natestemen closed 6 months ago

natestemen commented 8 months ago

Problem

The Pauli Twirling module mitiq.pt is currently difficult to use on a simulated backend. This is because incoherent/coherent noise is usually modeled using additional layers of noise that twirling does not account for.

>>> a, b = cirq.LineQubit.range(2)
>>> c = cirq.Circuit(cirq.H.on(a), cirq.CNOT.on(a, b))
>>> cn = c.with_noise(cirq.NoiseModel.from_noise_model_like(cirq.depolarize(p=0.01)))
>>> print(cn)
0: ───H───D(0.01)[cirq.VirtualTag()]───@───D(0.01)[cirq.VirtualTag()]───
                                       │
1: ───────D(0.01)[cirq.VirtualTag()]───X───D(0.01)[cirq.VirtualTag()]───

>>> print(mitiq.pt.pauli_twirl_circuit(cn, num_circuits=1)[0])
0: ───H───D(0.01)[cirq.VirtualTag()]───Y───@───X───D(0.01)[cirq.VirtualTag()]───
                                           │
1: ───────D(0.01)[cirq.VirtualTag()]───Y───X───Z───D(0.01)[cirq.VirtualTag()]───

Because the added twirling gates ($Y\otimes Y$ and $X\otimes Z$ in the case above) do not surround the depolarizing noise, this version of twirling does not actually tailor coherent noise into incoherent noise. In essence, this means mitiq.pt is useless on simulators, unless the user manually inserts noise after twirling.

Because $\mathrm{CNOT}$ / $\mathrm{CZ}$ gates themselves contain noise on hardware, this does not effect using mitiq.pt on a QPU.

Potential solutions

  1. We can allow the user to pass something like a noise_gate to apply in between the twirled gates and the $\mathrm{CNOT}$ / $\mathrm{CZ}$ gates. I don't love this idea since Mitiq is supposed to be completely unaware of the logic in the executor (which in itself could contain additional noise), but it might be the best way?
  2. We detect the presence of a noise model and apply twirling gates surrounding both the $\mathrm{CNOT}$ / $\mathrm{CZ}$ gates and the noise.
  3. We make it loud and clear that this technique does not work (in our implementation) on simulators.

cc @purva-thakre @bdg221 @Aaron-Robertson since we talked about this today.

andreamari commented 8 months ago

unless the user manually inserts noise after twirling

In my opinion this is the normal workflow. Isn't it? Noise is typically introduced as a final step during the execution on a (real or simulated) backend.

For example, ZNE, PEC, PT, etc.., all work by transforming (initially noiseless) circuits by unitary folding, gate insertion, twirling, etc. As a final step, modified circuits are executed on a noisy (real or simulated) backend and this is the moment in which noise is inserted into the circuit.

purva-thakre commented 8 months ago

@andreamari Yes, but the way Pauli Twirling currently works is by inserting the twirling operators before and after the CNOT gate only. If I insert noise on both qubits before twirling, the twirling operations are not applied after the noise layer. The noise doesn't get tailored in this scenario at all. This is why all my previous attempts at adding the PTM failed. I could not see the noise tailored in before and after twirling. The noise has to be tailored for pauli twirling to work.

Here's a link to a comment where I kinda figured this out: https://github.com/unitaryfund/mitiq/pull/2148#issuecomment-1935293516 Disregard the discussion where I talk about using some function where we have to splice the circuit first or switch to randomized compiling instead. The description of this issue is an easier solution in my opinion.

With the new proposed changes, we can see the coherent noise change over the different numbers of circuits used to average after twirling.

andreamari commented 8 months ago

I see that it is not possible to twirl a circuit with noisy gates, in the same way in which it is currently not possible to apply ZNE or PEC to circuits with noisy gates (noise is always introduced as a final step by the executor).

Mitiq could introduce support for input circuits with noisy gates if you think it is important, even though it seems hard to support multi-platform conversions of noisy channels.

On the other hand, it is still possible to observe the effect of twirling on a simulator with current code by simply adding noise as a final step, instead of adding noise as a first step. See e.g. the following example:

import cirq
import mitiq
from mitiq import pt

def add_noise_to_CNOT_gates(circuit): 
    """Add noise to CNOT gates."""
    # Create a noise model.
    noisy_circuit = cirq.Circuit()
    for moment in circuit:
        layer = cirq.Circuit()
        for op in moment:
            layer.append(op)
            if op.gate==cirq.ops.CNOT:
                layer.append(cirq.amplitude_damp(0.01).on_each(op.qubits))
        noisy_circuit += layer
    return noisy_circuit

a, b = cirq.LineQubit.range(2)

c = cirq.Circuit(cirq.H.on(a), cirq.CNOT.on(a, b))
c_twirled = pt.pauli_twirl_circuit(c, num_circuits=1)[0]

# Add noise to simulate a noisy backend
c_noisy = add_noise_to_CNOT_gates(c)
c_twirled_noisy = add_noise_to_CNOT_gates(c_twirled)

print("Circuits before adding noise")
print(c_noisy)
print(c_twirled)

print("Circuits after adding noise")
print(c_noisy)
print(c_twirled_noisy)
Circuits before adding noise
0: ───H───@───AD(0.01)───
          │
1: ───────X───AD(0.01)───
0: ───H───Y───@───X───
              │
1: ───────Z───X───Y───
Circuits after adding noise
0: ───H───@───AD(0.01)───
          │
1: ───────X───AD(0.01)───
0: ───H───Y───@───AD(0.01)───X───
              │
1: ───────Z───X───AD(0.01)───Y───

As you can see in the last circuit, twirling Pauli gates now surround the noisy CNOT as expected.

purva-thakre commented 8 months ago

On the other hand, it is still possible to observe the effect of twirling on a simulator with current code by simply adding noise as a final step

Yes, that's what we discussed as a possible solution. Step 1 of the issue description is similar to your defined function add_noise_to_CNOT_gates. Thanks @andreamari !

I am thinking of combining Solutions 1 and 3. Let the user insert noise instead of searching for a noise model used for the CZ/CNOT gates. The documentation will have to be clear that if this is used in a simulator, the user has to insert a noisy version of the CZ and CNOT gates.

cosenal commented 6 months ago

@Misty-W do we want to keep this open, or is it now superseded by #2346?

Misty-W commented 6 months ago

Superseded by https://github.com/unitaryfund/mitiq/issues/2346.