Noise and Error#

  • Incoherent error: loss of quantum information in the form of superposition and entanglement

  • coherent error in gate: incorrect Hamiltonian evolution

  • state preparation and readout (SPAM) errors

from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
from qiskit.quantum_info import Kraus, SuperOp, random_quantum_channel, PTM, Choi, Chi
from qiskit.visualization import plot_histogram
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

# Import from Qiskit Aer noise module
from qiskit_aer.noise import (
    NoiseModel,
    QuantumError,
    ReadoutError,
    depolarizing_error,
    pauli_error,
    thermal_relaxation_error,
)

Representation#

Pauli Transfer Matrix (PTM)#

https://docs.quantum.ibm.com/api/qiskit/qiskit.quantum_info.PTM

Pauli Transfer Matrix (PTM) representation of a Quantum Channel.

rqc = PTM(random_quantum_channel(2, 2))
print('num_qubits: ', rqc.num_qubits)
rqc
num_qubits:  1
PTM([[ 1.00000000e+00-9.52787305e-18j, -2.08166817e-17+0.00000000e+00j,
       2.77555756e-17+0.00000000e+00j, -3.33066907e-16-6.88837700e-18j],
     [-9.09489194e-02+0.00000000e+00j, -3.72195758e-01+0.00000000e+00j,
       2.01356134e-02+0.00000000e+00j, -1.88193128e-01+0.00000000e+00j],
     [ 1.07371749e-01+0.00000000e+00j,  3.69139869e-01+0.00000000e+00j,
      -1.20645755e-01+0.00000000e+00j, -4.81331497e-01+0.00000000e+00j],
     [-1.56748013e-01+8.54415943e-18j,  1.14009043e-01+0.00000000e+00j,
      -4.85784633e-01+0.00000000e+00j, -1.72685021e-01+4.62303219e-19j]],
    input_dims=(2,), output_dims=(2,))
rqc.compose(rqc)
PTM([[ 1.00000000e+00-2.40259894e-18j, -3.65286159e-17+1.06400890e-17j,
      -3.25381917e-17-3.48337848e-19j,  8.10475729e-17-2.95275781e-18j],
     [-5.03863959e-01-3.97212202e-18j,  8.72077238e-02+7.41407448e-18j,
      -4.36095725e-02-9.82187287e-18j, -2.73736239e-02+4.42821287e-18j],
     [ 3.66501244e-01+1.11459993e-17j, -5.33738689e-02-5.89698216e-18j,
       8.10998407e-02-1.25503009e-17j, -9.32187871e-02+2.08189613e-18j],
     [-2.18558871e-01-7.45593673e-18j,  5.86923694e-02-9.37442687e-18j,
       2.00888015e-01-1.19893761e-17j, -7.11955275e-03-7.38513938e-19j]],
    input_dims=(2,), output_dims=(2,))
rqc.power(4)
rqc.adjoint()
rqc.is_unitary()
rqc.is_cptp() # cp, tp, unitary
True

Kraus#

from qiskit.quantum_info import Kraus
rqc_kraus = Kraus(random_quantum_channel(2, 2))
rqc_kraus
Kraus([[[ 0.40797192-0.21907435j, -0.04302789-0.46760563j],
        [-0.02762658-0.30734793j, -0.02968974+0.05646335j]],

       [[-0.31594324-0.24582982j,  0.22465833-0.25842112j],
        [-0.29698492+0.15016866j,  0.33448024-0.03782379j]],

       [[ 0.29609313-0.1089395j ,  0.17119268-0.49213072j],
        [-0.19383795-0.15392175j,  0.30024441-0.14315255j]],

       [[ 0.15164906-0.06327016j,  0.22433035+0.00823283j],
        [ 0.47619079-0.06909437j, -0.07334134+0.32703776j]]],
      input_dims=(2,), output_dims=(2,))

Noise channel#

https://docs.quantum.ibm.com/guides/build-noise-models#build-noise-models

# Construct a 1-qubit bit-flip and phase-flip errors
p_error = 0.05
bit_flip = pauli_error([('X', p_error), ('I', 1 - p_error)])
phase_flip = pauli_error([('Z', p_error), ('I', 1 - p_error)])
print(bit_flip)
print(phase_flip)
QuantumError on 1 qubits. Noise circuits:
  P(0) = 0.05, Circuit = 
   ┌───┐
q: ┤ X ├
   └───┘
  P(1) = 0.95, Circuit = 
   ┌───┐
q: ┤ I ├
   └───┘
QuantumError on 1 qubits. Noise circuits:
  P(0) = 0.05, Circuit = 
   ┌───┐
q: ┤ Z ├
   └───┘
  P(1) = 0.95, Circuit = 
   ┌───┐
q: ┤ I ├
   └───┘
# Compose two bit-flip and phase-flip errors
bitphase_flip = bit_flip.compose(phase_flip)
print(bitphase_flip)
QuantumError on 1 qubits. Noise circuits:
  P(0) = 0.0025000000000000005, Circuit = 
   ┌───┐┌───┐
q: ┤ X ├┤ Z ├
   └───┘└───┘
  P(1) = 0.0475, Circuit = 
   ┌───┐┌───┐
q: ┤ X ├┤ I ├
   └───┘└───┘
  P(2) = 0.0475, Circuit = 
   ┌───┐┌───┐
q: ┤ I ├┤ Z ├
   └───┘└───┘
  P(3) = 0.9025, Circuit = 
   ┌───┐┌───┐
q: ┤ I ├┤ I ├
   └───┘└───┘
# Tensor product two bit-flip and phase-flip errors with
# bit-flip on qubit-0, phase-flip on qubit-1
error2 = phase_flip.tensor(bit_flip)
print(error2)
QuantumError on 2 qubits. Noise circuits:
  P(0) = 0.0025000000000000005, Circuit = 
     ┌───┐
q_0: ┤ X ├
     ├───┤
q_1: ┤ Z ├
     └───┘
  P(1) = 0.0475, Circuit = 
     ┌───┐
q_0: ┤ I ├
     ├───┤
q_1: ┤ Z ├
     └───┘
  P(2) = 0.0475, Circuit = 
     ┌───┐
q_0: ┤ X ├
     ├───┤
q_1: ┤ I ├
     └───┘
  P(3) = 0.9025, Circuit = 
     ┌───┐
q_0: ┤ I ├
     ├───┤
q_1: ┤ I ├
     └───┘

Converting to and from QuantumChannel operators#

# Convert to Kraus operator
bit_flip_kraus = Kraus(bit_flip)
print(bit_flip_kraus)
Kraus([[[ 9.74679434e-01+0.j,  0.00000000e+00+0.j],
        [ 0.00000000e+00+0.j,  9.74679434e-01+0.j]],

       [[ 0.00000000e+00+0.j,  2.23606798e-01+0.j],
        [ 2.23606798e-01+0.j, -4.96506831e-17+0.j]]],
      input_dims=(2,), output_dims=(2,))
# Convert to Superoperator
phase_flip_sop = SuperOp(phase_flip)
print(phase_flip_sop)
SuperOp([[1. +0.j, 0. +0.j, 0. +0.j, 0. +0.j],
         [0. +0.j, 0.9+0.j, 0. +0.j, 0. +0.j],
         [0. +0.j, 0. +0.j, 0.9+0.j, 0. +0.j],
         [0. +0.j, 0. +0.j, 0. +0.j, 1. +0.j]],
        input_dims=(2,), output_dims=(2,))
# Convert back to a quantum error
print(QuantumError(bit_flip_kraus))
 
# Check conversion is equivalent to original error
QuantumError(bit_flip_kraus) == bit_flip
QuantumError on 1 qubits. Noise circuits:
  P(0) = 1.0, Circuit = 
   ┌───────┐
q: ┤ kraus ├
   └───────┘
True

Readout error#

# Measurement misassignment probabilities
p0given1 = 0.1
p1given0 = 0.05
 
ReadoutError([[1 - p1given0, p1given0], [p0given1, 1 - p0given1]])
ReadoutError([[0.95 0.05]
 [0.1  0.9 ]])

Examples#

# System Specification
n_qubits = 4
circ = QuantumCircuit(n_qubits)
 
# Test Circuit
circ.h(0)
for qubit in range(n_qubits - 1):
    circ.cx(qubit, qubit + 1)
circ.measure_all()
print(circ)
        ┌───┐                ░ ┌─┐         
   q_0: ┤ H ├──■─────────────░─┤M├─────────
        └───┘┌─┴─┐           ░ └╥┘┌─┐      
   q_1: ─────┤ X ├──■────────░──╫─┤M├──────
             └───┘┌─┴─┐      ░  ║ └╥┘┌─┐   
   q_2: ──────────┤ X ├──■───░──╫──╫─┤M├───
                  └───┘┌─┴─┐ ░  ║  ║ └╥┘┌─┐
   q_3: ───────────────┤ X ├─░──╫──╫──╫─┤M├
                       └───┘ ░  ║  ║  ║ └╥┘
meas: 4/════════════════════════╩══╩══╩══╩═
                                0  1  2  3 
# Ideal simulator and execution
sim_ideal = AerSimulator()
result_ideal = sim_ideal.run(circ).result()
plot_histogram(result_ideal.get_counts(0))
_images/1cafd7e284e37ff2bf2fe470fbaf2b7b35110c319c629e22666e238916030162.png

from qiskit_aer import AerSimulator#

# Example error probabilities
p_reset = 0.03
p_meas = 0.1
p_gate1 = 0.05
 
# QuantumError objects
error_reset = pauli_error([('X', p_reset), ('I', 1 - p_reset)])
error_meas = pauli_error([('X',p_meas), ('I', 1 - p_meas)])
error_gate1 = pauli_error([('X',p_gate1), ('I', 1 - p_gate1)])
error_gate2 = error_gate1.tensor(error_gate1)
 
# Add errors to noise model
noise_bit_flip = NoiseModel()
noise_bit_flip.add_all_qubit_quantum_error(error_reset, "reset")
noise_bit_flip.add_all_qubit_quantum_error(error_meas, "measure")
noise_bit_flip.add_all_qubit_quantum_error(error_gate1, ["u1", "u2", "u3"])
noise_bit_flip.add_all_qubit_quantum_error(error_gate2, ["cx"])
 
print(noise_bit_flip)
NoiseModel:
  Basis gates: ['cx', 'id', 'rz', 'sx', 'u1', 'u2', 'u3']
  Instructions with noise: ['u1', 'u2', 'cx', 'u3', 'reset', 'measure']
  All-qubits errors: ['reset', 'measure', 'u1', 'u2', 'u3', 'cx']
# Create noisy simulator backend
sim_noise = AerSimulator(noise_model=noise_bit_flip)
 
# Transpile circuit for noisy basis gates
passmanager = generate_preset_pass_manager(optimization_level=3, backend=sim_noise)
circ_tnoise = passmanager.run(circ)
 
# Run and get counts
result_bit_flip = sim_noise.run(circ_tnoise).result()
counts_bit_flip = result_bit_flip.get_counts(0)
 
# Plot noisy output
plot_histogram(counts_bit_flip)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[32], line 5
      2 sim_noise = AerSimulator(noise_model=noise_bit_flip)
      4 # Transpile circuit for noisy basis gates
----> 5 passmanager = generate_preset_pass_manager(optimization_level=3, backend=sim_noise)
      6 circ_tnoise = passmanager.run(circ)
      8 # Run and get counts

NameError: name 'generate_preset_pass_manager' is not defined