This page was generated from the notebook examples/notebooks/6_Noise_Simulation.ipynb.

Simulating noise on MPQP

Quantum computers hold immense potential, but a major hurdle is the quantum noise. Noise refers to anything that disrupts a quantum computer’s calculations. Being able to simulate and study the behavior of quantum circuits under the effect of noise is crucial in the NISQ era.

In this Notebook, we describe how to run noisy simulations by defining noise models, adding them to a QCircuit, and finally run the circuit. In this notebook, we will focus, with a practical approach, on the simulation of Depolarizing, BitFlip, AmplitudeDamping and PhaseDamping noise models.

[1]:
from mpqp import QCircuit, Language
from mpqp.gates import *
from mpqp.noise import *
from mpqp.measures import BasisMeasure
from mpqp.execution import *

Instantiating a noise model

We define an abstract class NoiseModel representing noisy channels acting on the qubits of the circuit, either after each gate application, or as an interaction with the environement (what is called idle noise). Each predefined noise model should extend this class, which has common attributes targets (indicating the indices of the qubits affected by this noise model) and the optional gates (indicating specific gates after which the noise will be applied)

1- Depolarizing noise model

If one wants to apply a depolarizing noise on the circuit, he can use the class Depolarizing, which is extending the class DimensionalNoiseModel. We then can specify two additional argument: a first mandatory argument indicating the probability, or the error rate of the channel, and the dimension parameter allowing us to target specific gates within a quantum circuit.

First, we can define the Depolarizing model by providing a probability and the list of target qubits. One can target all qubits of a circuit or just select specific ones. By default, the parameter dimensionis equal to 1, and having a multi-qubit target will imply a tensor product of one qubit channels based on the instantiated depolarizing noise.

[2]:
Depolarizing(0.5, [0, 1, 2])
[2]:
Depolarizing(0.5, [0, 1, 2])

One can also precise a higher dimension when the number of target qubits allows it, and this can imply the application of several noise models. In fact, if the number of targets is higher than the dimension, we will consider all possible combinations of the target qubits that matches the depolarizing dimension. However, if the number of target qubits is equal to the dimension, a unique noise model will be applied.

[3]:
Depolarizing(0.1, [0, 1, 2], dimension=2)
[3]:
Depolarizing(0.1, [0, 1, 2], dimension=2)

If we want to attach a noise model to specific gates, we can specify them in a list and input them in the parameter gates. Then, the noise will be applied only after gates that appear in the list in parameter, and for which target qubits (in the sense of application of the unitary operation, so it includes control qubits) were precised in the second parameter targets. When precising the gates one has to give the class of the gate, extending NativeGate, and not an instance of the gate.

[4]:
Depolarizing(0.23, [2, 3], gates=[H, Rx, U])
[4]:
Depolarizing(0.23, [2, 3], gates=[H, Rx, U])

Note that in the previous example, only one-qubit gates were specified for the noise. If the dimension is higher, one has to input gates for which the size matches exactly the dimension.

[5]:
d = Depolarizing(0.45, [1, 3, 4], dimension=2, gates=[CNOT, CZ])
[6]:
d.to_other_language(Language.BRAKET)
[6]:
TwoQubitDepolarizing('probability': 0.45, 'qubit_count': 2)
[7]:
print(d.to_other_language(Language.QISKIT))
QuantumError on 2 qubits. Noise circuits:
  P(0) = 0.5781250000000003, Circuit =
     ┌────────────┐
q_0: ┤0           ├
     │  Pauli(II) │
q_1: ┤1           ├
     └────────────┘
  P(1) = 0.028125000000000018, Circuit =
     ┌────────────┐
q_0: ┤0           ├
     │  Pauli(IX) │
q_1: ┤1           ├
     └────────────┘
  P(2) = 0.028125000000000018, Circuit =
     ┌────────────┐
q_0: ┤0           ├
     │  Pauli(IY) │
q_1: ┤1           ├
     └────────────┘
  P(3) = 0.028125000000000018, Circuit =
     ┌────────────┐
q_0: ┤0           ├
     │  Pauli(IZ) │
q_1: ┤1           ├
     └────────────┘
  P(4) = 0.028125000000000018, Circuit =
     ┌────────────┐
q_0: ┤0           ├
     │  Pauli(XI) │
q_1: ┤1           ├
     └────────────┘
  P(5) = 0.028125000000000018, Circuit =
     ┌────────────┐
q_0: ┤0           ├
     │  Pauli(XX) │
q_1: ┤1           ├
     └────────────┘
  P(6) = 0.028125000000000018, Circuit =
     ┌────────────┐
q_0: ┤0           ├
     │  Pauli(XY) │
q_1: ┤1           ├
     └────────────┘
  P(7) = 0.028125000000000018, Circuit =
     ┌────────────┐
q_0: ┤0           ├
     │  Pauli(XZ) │
q_1: ┤1           ├
     └────────────┘
  P(8) = 0.028125000000000018, Circuit =
     ┌────────────┐
q_0: ┤0           ├
     │  Pauli(YI) │
q_1: ┤1           ├
     └────────────┘
  P(9) = 0.028125000000000018, Circuit =
     ┌────────────┐
q_0: ┤0           ├
     │  Pauli(YX) │
q_1: ┤1           ├
     └────────────┘
  P(10) = 0.028125000000000018, Circuit =
     ┌────────────┐
q_0: ┤0           ├
     │  Pauli(YY) │
q_1: ┤1           ├
     └────────────┘
  P(11) = 0.028125000000000018, Circuit =
     ┌────────────┐
q_0: ┤0           ├
     │  Pauli(YZ) │
q_1: ┤1           ├
     └────────────┘
  P(12) = 0.028125000000000018, Circuit =
     ┌────────────┐
q_0: ┤0           ├
     │  Pauli(ZI) │
q_1: ┤1           ├
     └────────────┘
  P(13) = 0.028125000000000018, Circuit =
     ┌────────────┐
q_0: ┤0           ├
     │  Pauli(ZX) │
q_1: ┤1           ├
     └────────────┘
  P(14) = 0.028125000000000018, Circuit =
     ┌────────────┐
q_0: ┤0           ├
     │  Pauli(ZY) │
q_1: ┤1           ├
     └────────────┘
  P(15) = 0.028125000000000018, Circuit =
     ┌────────────┐
q_0: ┤0           ├
     │  Pauli(ZZ) │
q_1: ┤1           ├
     └────────────┘
[8]:
## QuantumChannel representation
d.to_other_language(Language.QISKIT).to_quantumchannel()
[8]:
SuperOp([[0.6625+0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.1125+0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.1125+0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.1125+0.j],
         [0.    +0.j, 0.55  +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j],
         [0.    +0.j, 0.    +0.j, 0.55  +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j],
         [0.    +0.j, 0.    +0.j, 0.    +0.j, 0.55  +0.j, 0.    +0.j,
          0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j],
         [0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.55  +0.j,
          0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j],
         [0.1125+0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.6625+0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.1125+0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.1125+0.j],
         [0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j, 0.55  +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j],
         [0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j, 0.    +0.j, 0.55  +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j],
         [0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j, 0.    +0.j, 0.    +0.j, 0.55  +0.j, 0.    +0.j,
          0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j],
         [0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.55  +0.j,
          0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j],
         [0.1125+0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.1125+0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.6625+0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.1125+0.j],
         [0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j, 0.55  +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j],
         [0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j, 0.    +0.j, 0.55  +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j],
         [0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j, 0.    +0.j, 0.    +0.j, 0.55  +0.j, 0.    +0.j,
          0.    +0.j],
         [0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.55  +0.j,
          0.    +0.j],
         [0.1125+0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.1125+0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.1125+0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j, 0.    +0.j,
          0.6625+0.j]],
        input_dims=(2, 2), output_dims=(2, 2))
[9]:
print(d.to_other_language(Language.MY_QLM))
Depolarizing channel, p = 0.45:
[[0.74161985 0.         0.         0.        ]
 [0.         0.74161985 0.         0.        ]
 [0.         0.         0.74161985 0.        ]
 [0.         0.         0.         0.74161985]]
[[0.        +0.j 0.17320508+0.j 0.        +0.j 0.        +0.j]
 [0.17320508+0.j 0.        +0.j 0.        +0.j 0.        +0.j]
 [0.        +0.j 0.        +0.j 0.        +0.j 0.17320508+0.j]
 [0.        +0.j 0.        +0.j 0.17320508+0.j 0.        +0.j]]
[[0.+0.j         0.-0.17320508j 0.+0.j         0.+0.j        ]
 [0.+0.17320508j 0.+0.j         0.+0.j         0.+0.j        ]
 [0.+0.j         0.+0.j         0.+0.j         0.-0.17320508j]
 [0.+0.j         0.+0.j         0.+0.17320508j 0.+0.j        ]]
[[ 0.17320508+0.j  0.        +0.j  0.        +0.j  0.        +0.j]
 [ 0.        +0.j -0.17320508+0.j  0.        +0.j -0.        +0.j]
 [ 0.        +0.j  0.        +0.j  0.17320508+0.j  0.        +0.j]
 [ 0.        +0.j -0.        +0.j  0.        +0.j -0.17320508+0.j]]
[[0.        +0.j 0.        +0.j 0.17320508+0.j 0.        +0.j]
 [0.        +0.j 0.        +0.j 0.        +0.j 0.17320508+0.j]
 [0.17320508+0.j 0.        +0.j 0.        +0.j 0.        +0.j]
 [0.        +0.j 0.17320508+0.j 0.        +0.j 0.        +0.j]]
[[0.        +0.j 0.        +0.j 0.        +0.j 0.17320508+0.j]
 [0.        +0.j 0.        +0.j 0.17320508+0.j 0.        +0.j]
 [0.        +0.j 0.17320508+0.j 0.        +0.j 0.        +0.j]
 [0.17320508+0.j 0.        +0.j 0.        +0.j 0.        +0.j]]
[[0.+0.j         0.+0.j         0.+0.j         0.-0.17320508j]
 [0.+0.j         0.+0.j         0.+0.17320508j 0.+0.j        ]
 [0.+0.j         0.-0.17320508j 0.+0.j         0.+0.j        ]
 [0.+0.17320508j 0.+0.j         0.+0.j         0.+0.j        ]]
[[ 0.        +0.j  0.        +0.j  0.17320508+0.j  0.        +0.j]
 [ 0.        +0.j -0.        +0.j  0.        +0.j -0.17320508+0.j]
 [ 0.17320508+0.j  0.        +0.j  0.        +0.j  0.        +0.j]
 [ 0.        +0.j -0.17320508+0.j  0.        +0.j -0.        +0.j]]
[[0.+0.j         0.+0.j         0.-0.17320508j 0.+0.j        ]
 [0.+0.j         0.+0.j         0.+0.j         0.-0.17320508j]
 [0.+0.17320508j 0.+0.j         0.+0.j         0.+0.j        ]
 [0.+0.j         0.+0.17320508j 0.+0.j         0.+0.j        ]]
[[0.+0.j         0.+0.j         0.+0.j         0.-0.17320508j]
 [0.+0.j         0.+0.j         0.-0.17320508j 0.+0.j        ]
 [0.+0.j         0.+0.17320508j 0.+0.j         0.+0.j        ]
 [0.+0.17320508j 0.+0.j         0.+0.j         0.+0.j        ]]
[[ 0.        +0.j  0.        +0.j  0.        +0.j -0.17320508+0.j]
 [ 0.        +0.j  0.        +0.j  0.17320508+0.j  0.        +0.j]
 [ 0.        +0.j  0.17320508+0.j  0.        +0.j  0.        +0.j]
 [-0.17320508+0.j  0.        +0.j  0.        +0.j  0.        +0.j]]
[[ 0.+0.j          0.+0.j          0.-0.17320508j  0.+0.j        ]
 [ 0.+0.j         -0.+0.j          0.+0.j          0.+0.17320508j]
 [ 0.+0.17320508j  0.+0.j          0.+0.j          0.+0.j        ]
 [ 0.+0.j          0.-0.17320508j  0.+0.j         -0.+0.j        ]]
[[ 0.17320508+0.j  0.        +0.j  0.        +0.j  0.        +0.j]
 [ 0.        +0.j  0.17320508+0.j  0.        +0.j  0.        +0.j]
 [ 0.        +0.j  0.        +0.j -0.17320508+0.j -0.        +0.j]
 [ 0.        +0.j  0.        +0.j -0.        +0.j -0.17320508+0.j]]
[[ 0.        +0.j  0.17320508+0.j  0.        +0.j  0.        +0.j]
 [ 0.17320508+0.j  0.        +0.j  0.        +0.j  0.        +0.j]
 [ 0.        +0.j  0.        +0.j -0.        +0.j -0.17320508+0.j]
 [ 0.        +0.j  0.        +0.j -0.17320508+0.j -0.        +0.j]]
[[ 0.+0.j          0.-0.17320508j  0.+0.j          0.+0.j        ]
 [ 0.+0.17320508j  0.+0.j          0.+0.j          0.+0.j        ]
 [ 0.+0.j          0.+0.j         -0.+0.j          0.+0.17320508j]
 [ 0.+0.j          0.+0.j          0.-0.17320508j -0.+0.j        ]]
[[ 0.17320508+0.j  0.        +0.j  0.        +0.j  0.        +0.j]
 [ 0.        +0.j -0.17320508+0.j  0.        +0.j -0.        +0.j]
 [ 0.        +0.j  0.        +0.j -0.17320508+0.j -0.        +0.j]
 [ 0.        +0.j -0.        +0.j -0.        +0.j  0.17320508+0.j]]

2- BitFlip noise model

To apply a bit flip noise on the circuit, we use the class BitFlip, which is extending the class NoiseModel. We then can specify a mandatory argument indicating the probability (the error rate of the channel).

First, we can define the BitFlip model by providing a probability and the list of target qubits. One can target all qubits of a circuit or just select specific ones and having a multi-qubit target will imply the application of single-qubit bit flip channels to each qubit associated with the specified gates.

[10]:
BitFlip(0.4, [0, 1, 2])
[10]:
BitFlip(0.4, [0, 1, 2])
[11]:
BitFlip(0.2, [1, 2], gates=[H, Rx])
[11]:
BitFlip(0.2, [1, 2], gates=[H, Rx])

If no targets are specified, the bit flip noise will be applied to all qubits in the circuit.

[12]:
print(BitFlip(0.5))
BitFlip(0.5)
[13]:
bf = BitFlip(0.45, [1, 3, 4], gates=[CNOT, CZ])
[14]:
bf.to_other_language(Language.BRAKET)
[14]:
BitFlip('probability': 0.45, 'qubit_count': 1)
[15]:
print(bf.to_other_language(Language.QISKIT))
QuantumError on 1 qubits. Noise circuits:
  P(0) = 0.45, Circuit =
   ┌───┐
q: ┤ X ├
   └───┘
  P(1) = 0.55, Circuit =
   ┌───┐
q: ┤ I ├
   └───┘
[16]:
## QuantumChannel representation
bf.to_other_language(Language.QISKIT).to_quantumchannel()
[16]:
SuperOp([[0.55+0.j, 0.  +0.j, 0.  +0.j, 0.45+0.j],
         [0.  +0.j, 0.55+0.j, 0.45+0.j, 0.  +0.j],
         [0.  +0.j, 0.45+0.j, 0.55+0.j, 0.  +0.j],
         [0.45+0.j, 0.  +0.j, 0.  +0.j, 0.55+0.j]],
        input_dims=(2,), output_dims=(2,))

3- AmplitudeDamping noise model

We can define the AmplitudeDamping model by providing the decay rate gamma. Another parameter is the probability of environment excitation prob, which default value is one (which represents the standard amplitude damping noise). When it’s not one, that means the excitation is spontaneous which represents the generalized form of the amplitude damping noise.

One can target all qubits of a circuit or just select specific ones. If no targets are specified, the noise model will be applied to all qubits in the circuit.

[17]:
AmplitudeDamping(0.3, targets=[0, 1, 2])
[17]:
AmplitudeDamping(0.3, targets=[0, 1, 2])
[18]:
AmplitudeDamping(0.23, 0.6, [2, 3], gates=[H, Rx, U])
[18]:
AmplitudeDamping(0.23, 0.6, targets=[2, 3], gates=[H, Rx, U])

If no targets are specified, the bit flip noise will be applied to all qubits in the circuit.

[19]:
print(AmplitudeDamping(0.5))
AmplitudeDamping(0.5)
[20]:
print(AmplitudeDamping(0.5, 0.3))
AmplitudeDamping(0.5, 0.3)
[21]:
ad = AmplitudeDamping(0.23, targets=[2, 3], gates=[H, Rx, U])
[22]:
ad.to_other_language(Language.BRAKET)
[22]:
AmplitudeDamping('gamma': 0.23, 'qubit_count': 1)
[23]:
## QuantumChannel representation
ad.to_other_language(Language.QISKIT).to_quantumchannel()
[23]:
SuperOp([[1.        +0.j, 0.        +0.j, 0.        +0.j, 0.23      +0.j],
         [0.        +0.j, 0.87749644+0.j, 0.        +0.j, 0.        +0.j],
         [0.        +0.j, 0.        +0.j, 0.87749644+0.j, 0.        +0.j],
         [0.        +0.j, 0.        +0.j, 0.        +0.j, 0.77      +0.j]],
        input_dims=(2,), output_dims=(2,))
[24]:
gad = AmplitudeDamping(0.23, 0.6, [2, 3], gates=[H, Rx, U])
[25]:
gad.to_other_language(Language.BRAKET)
[25]:
GeneralizedAmplitudeDamping('gamma': 0.23, 'probability': 0.6, 'qubit_count': 1)
[26]:
## QuantumChannel representation
gad.to_other_language(Language.QISKIT).to_quantumchannel()
[26]:
SuperOp([[0.908     +0.j, 0.        +0.j, 0.        +0.j, 0.138     +0.j],
         [0.        +0.j, 0.87749644+0.j, 0.        +0.j, 0.        +0.j],
         [0.        +0.j, 0.        +0.j, 0.87749644+0.j, 0.        +0.j],
         [0.092     +0.j, 0.        +0.j, 0.        +0.j, 0.862     +0.j]],
        input_dims=(2,), output_dims=(2,))

4- PhaseDamping noise model

We can define the PhaseDamping noise model by providing the decoherence rate gamma, which represents the probability of phase error occurring during the quantum operation. PhaseDamping noise model introduces phase errors into a quantum system caused by interactions with the environment, leading to decoherence but leaving the energy of the system unchanged.

Similar to other noise models, One can target all qubits of a circuit or just select specific ones. If no targets are specified, the noise model will be applied to all qubits in the circuit.

[27]:
PhaseDamping(0.45, targets=[0, 1, 2])
[27]:
PhaseDamping(0.45, [0, 1, 2])
[28]:
PhaseDamping(0.2, [1, 2], gates=[H, CNOT, Z])
[28]:
PhaseDamping(0.2, [1, 2], gates=[H, CNOT, Z])

no targets specified, the phase damping error will be applied to all qubits in the circuit.

[29]:
PhaseDamping(0.2)
[29]:
PhaseDamping(0.2)
[30]:
pd = PhaseDamping(0.2, [1, 2], gates=[H, CNOT, Z])
[31]:
pd.to_other_language(Language.BRAKET)
[31]:
PhaseDamping('gamma': 0.2, 'qubit_count': 1)
[32]:
## QuantumChannel representation
pd.to_other_language(Language.QISKIT).to_quantumchannel()
[32]:
SuperOp([[1.        +0.j, 0.        +0.j, 0.        +0.j, 0.        +0.j],
         [0.        +0.j, 0.89442719+0.j, 0.        +0.j, 0.        +0.j],
         [0.        +0.j, 0.        +0.j, 0.89442719+0.j, 0.        +0.j],
         [0.        +0.j, 0.        +0.j, 0.        +0.j, 1.        +0.j]],
        input_dims=(2,), output_dims=(2,))
[33]:
print(pd.to_other_language(Language.MY_QLM))
Phase Damping channel, gamma = 0.2:
[[1.         0.        ]
 [0.         0.89442719]]
[[0.         0.        ]
 [0.         0.89442719]]

In summary, the application of the noise model can be tailored based on the specification of gates and targets, ranging from a global application (all gates, all qubits) to a targeted approach (specific gates affecting only selected qubits)

Adding noise to the circuit

Once we define the desired noise models, we have to attach them to the circuit. One way of doing this is by instantiating directly the circuit with the list of Instruction and NoiseModel.

[34]:
circuit_1 = QCircuit([H(0), CNOT(0,1), Y(1), BasisMeasure([0,1], shots=100),
                      BitFlip(0.3, [0], gates=[H]),
                      AmplitudeDamping(0.3, 0.6, [0, 1], gates=[H]),
                      Depolarizing(0.3, [0], gates=[H]),
                      PhaseDamping(0.3, [1])])

print(circuit_1)
     ┌───┐          ┌─┐
q_0: ┤ H ├──■───────┤M├───
     └───┘┌─┴─┐┌───┐└╥┘┌─┐
q_1: ─────┤ X ├┤ Y ├─╫─┤M├
          └───┘└───┘ ║ └╥┘
c: 2/════════════════╩══╩═
                     0  1
NoiseModel:
    BitFlip(0.3, [0], gates=[H])
    AmplitudeDamping(0.3, 0.6, targets=[0, 1], gates=[H])
    Depolarizing(0.3, [0], gates=[H])
    PhaseDamping(0.3, [1])

One can also use the method add(...) on an already instantiated QCircuit.

[35]:
circuit_2 = QCircuit([H(0), CNOT(0,1), Y(1), BasisMeasure([0,1], shots=100)])
circuit_2.add([Depolarizing(0.08, [0]), AmplitudeDamping(0.4, targets=[0]), BitFlip(0.13, [1]), PhaseDamping(0.45, [0, 1])])
circuit_2.pretty_print()
QCircuit : Size (Qubits, Cbits) = (2, 2), Nb instructions = 4
Depolarizing noise: on qubit 0 with probability 0.08
AmplitudeDamping noise: on qubit 0 with gamma 0.4
BitFlip noise: on qubit 1 with probability 0.13
PhaseDamping noise: with gamma 0.45
     ┌───┐          ┌─┐
q_0: ┤ H ├──■───────┤M├───
     └───┘┌─┴─┐┌───┐└╥┘┌─┐
q_1: ─────┤ X ├┤ Y ├─╫─┤M├
          └───┘└───┘ ║ └╥┘
c: 2/════════════════╩══╩═
                     0  1

We can get the list of noise models attached to a circuit using the noises attributes.

[36]:
print(circuit_2.noises)
[Depolarizing(0.08, [0]), AmplitudeDamping(0.4, targets=[0]), BitFlip(0.13, [1]), PhaseDamping(0.45, [0, 1])]

One can also retrieve the circuit without any noise model.

[37]:
circuit_2.without_noises()
[37]:
QCircuit([H(0), CNOT(0, 1), Y(1), BasisMeasure([0, 1], shots=100)], nb_qubits=2, nb_cbits=2, label="None")

When translating a QCircuit to another SDK’s circuit, if the noise is directly defined within the circuit, we also include the attached noise models. It is the case for AWS Braket Circuits.

[38]:
noisy_braket_circuit = circuit_2.to_other_language(Language.BRAKET)
print(noisy_braket_circuit)
/home/muham/colibri/mpqp/mpqp/mpqp/qasm/qasm_to_braket.py:107: UnsupportedBraketFeaturesWarning:
This program uses OpenQASM language features that may not be supported on QPUs or on-demand simulators.
  warnings.warn(

T  : │                      0                      │                      1                       │               2               │
      ┌───┐ ┌──────────┐ ┌─────────┐ ┌────────────┐       ┌──────────┐ ┌─────────┐  ┌────────────┐
q0 : ─┤ H ├─┤ PD(0.45) ├─┤ AD(0.4) ├─┤ DEPO(0.08) ├───●───┤ PD(0.45) ├─┤ AD(0.4) ├──┤ DEPO(0.08) ├─────────────────────────────────
      └───┘ └──────────┘ └─────────┘ └────────────┘   │   └──────────┘ └─────────┘  └────────────┘
                                                    ┌─┴─┐ ┌──────────┐ ┌──────────┐                ┌───┐ ┌──────────┐ ┌──────────┐
q1 : ───────────────────────────────────────────────┤ X ├─┤ PD(0.45) ├─┤ BF(0.13) ├────────────────┤ Y ├─┤ PD(0.45) ├─┤ BF(0.13) ├─
                                                    └───┘ └──────────┘ └──────────┘                └───┘ └──────────┘ └──────────┘
T  : │                      0                      │                      1                       │               2               │
[39]:
from mpqp.execution.providers.ibm import generate_qiskit_noise_model

qiskit_noise_model, noisy_qiskit_circuit = generate_qiskit_noise_model(circuit_2)
print(qiskit_noise_model)
print(noisy_qiskit_circuit)
WARNING: quantum error already exists for instruction "h" on qubits (1,) , appending additional error.
WARNING: quantum error already exists for instruction "h" on qubits (1,) , appending additional error.
WARNING: quantum error already exists for instruction "y" on qubits (0,) , appending additional error.
NoiseModel:
  Basis gates: ['cx', 'h', 'id', 'rz', 'sx', 'y']
  Instructions with noise: ['h', 'noisy_identity_1', 'y', 'noisy_identity_0', 'cx', 'noisy_identity_2']
  Qubits with noise: [0, 1]
  Specific qubit errors: [('h', (1,)), ('noisy_identity_0', (1,)), ('noisy_identity_1', (1,)), ('noisy_identity_2', (0,)), ('y', (0,)), ('cx', (1, 0))]
     ┌───┐     ┌───┐┌───┐┌─┐
q_0: ┤ H ├──■──┤ I ├┤ I ├┤M├───
     └───┘┌─┴─┐├───┤├───┤└╥┘┌─┐
q_1: ─────┤ X ├┤ I ├┤ Y ├─╫─┤M├
          └───┘└───┘└───┘ ║ └╥┘
c: 2/═════════════════════╩══╩═
                          0  1
NoiseModel:
    Depolarizing(0.08, [0])
    AmplitudeDamping(0.4, targets=[0])
    BitFlip(0.13, [1])
    PhaseDamping(0.45, [0, 1])

Running noisy circuits

Once we defined our noisy circuit, we would eventually like to simulate the circuit on a noisy simulator. For this example, we will focus on AWS Braket and IBM Qiskit devices.

All AvailableDevice must implemented a method called is_noisy_simulator(), indicating wether a given device can simulate noisy circuit.

[40]:
for device in AWSDevice:
    print(device.name, "|", device.is_noisy_simulator())
BRAKET_LOCAL_SIMULATOR | True
BRAKET_SV1_SIMULATOR | False
BRAKET_DM1_SIMULATOR | True
BRAKET_TN1_SIMULATOR | False
BRAKET_IONQ_HARMONY | False
BRAKET_IONQ_ARIA_1 | False
BRAKET_IONQ_ARIA_2 | False
BRAKET_IONQ_FORTE_1 | False
BRAKET_OQC_LUCY | False
QUERA_AQUILA | False
BRAKET_RIGETTI_ASPEN_M_3 | False
[41]:
for device in IBMDevice:
    print(device.name, "|", device.is_noisy_simulator())
AER_SIMULATOR | True
AER_SIMULATOR_STATEVECTOR | True
AER_SIMULATOR_DENSITY_MATRIX | True
AER_SIMULATOR_STABILIZER | True
AER_SIMULATOR_EXTENDED_STABILIZER | True
AER_SIMULATOR_MATRIX_PRODUCT_STATE | True
IBM_BRISBANE | False
IBM_OSAKA | False
IBM_KYOTO | False
IBM_SHERBROOKE | False
IBM_KYIV | False
IBM_NAZCA | False
IBM_CUSCO | False
IBM_ITHACA | False
IBM_TORINO | False
IBM_QUEBEC | False
IBM_KAWASAKI | False
IBM_CLEVELAND | False
IBM_CAIRO | False
IBM_HANOI | False
IBM_ALGIERS | False
IBM_KOLKATA | False
IBM_MUMBAI | False
IBM_PEEKSKILL | False
IBM_LEAST_BUSY | False

For running the noisy circuit, we use the exact same way as in the noiseless case: we just call the run function with the circuit and the requested devices.

[42]:
result = run(circuit_2, [AWSDevice.BRAKET_LOCAL_SIMULATOR, IBMDevice.AER_SIMULATOR]) # this line is valid for both noisy and non noisy cases
print(result)

WARNING: quantum error already exists for instruction "h" on qubits (1,) , appending additional error.
WARNING: quantum error already exists for instruction "h" on qubits (1,) , appending additional error.
WARNING: quantum error already exists for instruction "y" on qubits (0,) , appending additional error.
BatchResult: 2 results
Result: circuit 1, AWSDevice, BRAKET_LOCAL_SIMULATOR
 Counts: [34, 42, 14, 10]
 Probabilities: [0.34, 0.42, 0.14, 0.1]
 Samples:
  State: 00, Index: 0, Count: 34, Probability: 0.34
  State: 01, Index: 1, Count: 42, Probability: 0.42
  State: 10, Index: 2, Count: 14, Probability: 0.14
  State: 11, Index: 3, Count: 10, Probability: 0.1
 Error: None
Result: circuit 1, IBMDevice, AER_SIMULATOR
 Counts: [34, 47, 13, 6]
 Probabilities: [0.34, 0.47, 0.13, 0.06]
 Samples:
  State: 00, Index: 0, Count: 34, Probability: 0.34
  State: 01, Index: 1, Count: 47, Probability: 0.47
  State: 10, Index: 2, Count: 13, Probability: 0.13
  State: 11, Index: 3, Count: 6, Probability: 0.06
 Error: None
[ ]: