A Noisy Quantum Fourier Transform on the Quantum Learning Machine¶

This notebook demonstrates how to study the effects of noise on a quantum algorithm, for a specific hardware.

IBM Quantum Experience: 5 superconducting qubits¶

No description has been provided for this image No description has been provided for this image

Part I: Compilation¶

Step 1: Writing a quantum program¶

QLM provides usual quantum gates + library of quantum routines. Example: Quantum Fourier transform, key ingredient to e.g Shor algorithm.

In [1]:
from qat.lang.AQASM import Program
from qat.lang.AQASM.qftarith import QFT
nqbits = 5
prog = Program()
reg = prog.qalloc(nqbits)
prog.apply(QFT(nqbits), reg)
qft_circuit = prog.to_circ(inline=True) #convert program to circuit
qft_circuit.display()
No description has been provided for this image

Step 2: Optimization for IBM hardware using QLM optimizers¶

IBM QX4 has a limited qubit connectivity, and accepts only CNOT gates for two-qubit operations: No description has been provided for this image

In [2]:
from qat.core.wrappers.hardware_specs import Topology, HardwareSpecs
from qat.comm.hardware.ttypes import TopologyType
import json

topology = Topology(is_directed=True)
for control, target in [(1, 0), (2, 0), (2, 1), (2, 4), (3, 4), (3, 2)]:
    topology.add_edge(control, target)
hw_specs = HardwareSpecs(nbqbits=nqbits, topology=topology)
In [3]:
from qat.core.simutil import optimize_circuit
from qat.plugins import Nnizer, GateRewriter, Graphopt
qft_ibm_connectivity_circ = optimize_circuit(qft_circuit, Nnizer(), specs=hw_specs)

from qat.pbo import VAR
compiler = GateRewriter()
theta = VAR()
compiler.add_gate("C-PH",
                  variables=[theta],
                  pattern=[("CNOT", [0, 1]),  ("PH", [1], -theta / 2),  ("CNOT", [0, 1]),
                           ("PH", [1], theta / 2), ("PH", [0], theta)]
                 )

qft_ibm_connect_gates_circ = optimize_circuit(qft_ibm_connectivity_circ, compiler)
    
qft_ibm_optimized_circ = optimize_circuit(qft_ibm_connect_gates_circ, Graphopt(directed=True))

%matplotlib inline
import numpy as np, matplotlib.pyplot as plt
plt.bar([0, 1, 2, 3],  [len(c.ops) for c in [qft_circuit, qft_ibm_connectivity_circ, qft_ibm_connect_gates_circ, qft_ibm_optimized_circ]])
plt.xticks([0, 1, 2, 3], ["0-universal", "1-IBM connec.", "2-IBM connec. + gates", "3-IBM optimized"])
plt.xticks(rotation=45); plt.ylabel("Number of gates"); plt.grid();
No description has been provided for this image
In [4]:
#reminder:*
print(type(qft_ibm_optimized_circ))
qft_ibm_optimized_circ.display()
<class 'qat.core.wrappers.circuit.Circuit'>
No description has been provided for this image

Part II: Noisy simulation¶

No description has been provided for this image

Live simulation of noisy simulation on Atos QLM, with simplified hardware model:

  • Gates specs: assuming perfect gates
  • Environment: noise during qubit idling periods

Step 1: Define the Hardware¶

1.1 Gate specification¶

  • gate times: using gate times of IBM QX4 (available online)
  • gates: assuming ideal (=default) gates
  • One could also specify qubit-specific gate times
In [5]:
gate_times = {"H":60, "X":120, "Y":120, "S":1,"T":1, 'D-T':1, "Z":1, "RZ":lambda angle : 1, "PH": lambda angle : 1, "CNOT":386,
              "SWAP":150, "C-PH":lambda angle :150, "C-T":150, "C-S":150} #not in IBM but needed for comparison

from qat.hardware import DefaultGatesSpecification
ibm_gates_spec = DefaultGatesSpecification(gate_times)

1.2: Environment: definition of a noise model for idle qubits¶

  • Simple models for "idle" qubits: amplitude damping (A.D) and pure dephasing (P.D).

  • Choose between Lindblad approximation (with $T_1$ and $T_2$ taken from IBM), or take a specific noise spectral function for P.D channel, e.g 1/f noise: $$ J_z(\omega) = \frac{1}{\omega}\;\; (\omega_\mathrm{ir} < \omega < \omega_c)$$

  • One could also give one's own time-dependent Kraus operators $\{E_k(t)\}$.

In [6]:
from qat.quops import ParametricPureDephasing, ParametricAmplitudeDamping
T1, T2 = 44000, 38900 #nanosecs

#Amplitude Damping in Lindblad approximation
AD_Lindblad = ParametricAmplitudeDamping(T_1 = T1)

#Pure Dephasing: Lindblad approx. or give spectral function 
PD_Lindblad = ParametricPureDephasing(T_phi = 1/(1/T2 - 1/(2*T1)))
PD_1_over_f = ParametricPureDephasing(spectral_function = {"type": "1/f", 
                                                           "intensity": 3e-10,
                                                           "w_c" : 1e-1, #100MHz
                                                           "w_ir": 1e-9}) #1Hz
In [7]:
times = np.linspace(1, 50000, 1000)
plt.title("Evolution of the error probability vs idling time")
for channel in [PD_Lindblad, PD_1_over_f, AD_Lindblad]:
    plt.plot(times, [channel.prob(t) for t in times], label = str(channel))
plt.legend();
plt.ylim(0,1)
plt.xlabel("Time (ns)");
No description has been provided for this image

1.3 Combine gate specs + environment definition: hardware¶

Here: two different models for the environment (white vs 1/f spectrum for pure dephasing noise)

In [8]:
from qat.hardware import HardwareModel

ibm_hardware_lindblad = HardwareModel(ibm_gates_spec, idle_noise=[AD_Lindblad, PD_Lindblad])

ibm_hardware_1_over_f = HardwareModel(ibm_gates_spec, idle_noise=[AD_Lindblad, PD_1_over_f])

Step 2: Noisy circuit construction¶

No description has been provided for this image

Noisy circuit construction¶

In [9]:
from qat.noisy.noisy_circuit import construct_noisy_circuit
noisy_circuit_lindblad = construct_noisy_circuit(qft_ibm_optimized_circ, ibm_hardware_lindblad)

qft_ibm_optimized_circ.display(hardware_model=ibm_hardware_lindblad)
/tmp/ipykernel_42210/348325755.py:4: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator.
  qft_ibm_optimized_circ.display(hardware_model=ibm_hardware_lindblad)
No description has been provided for this image

Step 3: Noisy circuit simulation: fidelity w.r.t ideal circuit¶

In [10]:
from qat.noisy import compute_fidelity
from qat.qpus import NoisyQProc

### Simulation with Lindblad exponential decay ######
print("Fidelity AD + PD (Lindblad): ", compute_fidelity(qft_ibm_optimized_circ,
                                                        NoisyQProc(hardware_model=ibm_hardware_lindblad)))

### Simulation with  1/f noise + Lindblad ###########
print("Fidelity AD + PD (1/f): ", compute_fidelity(qft_ibm_optimized_circ,
                                                   NoisyQProc(hardware_model=ibm_hardware_1_over_f)))
Fidelity AD + PD (Lindblad):  (np.float64(0.8346332174273724), None)
Fidelity AD + PD (1/f):  (np.float64(0.8822735375957763), None)

Influence of limited connectivity and gateset on fidelity¶

In [11]:
fidelities = [compute_fidelity(circuit, NoisyQProc(hardware_model=ibm_hardware_lindblad))[0]
              for circuit in [qft_circuit, qft_ibm_connectivity_circ,
                              qft_ibm_connect_gates_circ,
                              qft_ibm_optimized_circ]]
plt.bar([1, 2, 3, 4], fidelities);
plt.xticks([1, 2, 3, 4], ["0-universal", "1-IBM connec.", "2-IBM connec. + gates", "3-IBM optimized"])
plt.xticks(rotation=45); plt.ylabel("Fidelity")
plt.grid();plt.ylim(0.8,1);
No description has been provided for this image

Influence of coherence times on fidelity¶

In [12]:
def compute_T1_T2_effect(T1 = np.inf, T2 = np.inf):
    idle_noise = []
    if not T1 == np.inf: idle_noise.append(ParametricAmplitudeDamping(T_1 = T1))
    if not T2 == np.inf: idle_noise.append(ParametricPureDephasing(T_phi = 1/(1/T2 - 1/(2*T1))))
    ibm_hardware = HardwareModel(ibm_gates_spec, idle_noise = idle_noise)
    qpu = NoisyQProc(hardware_model=ibm_hardware)
    return compute_fidelity(qft_ibm_optimized_circ, qpu)[0]
    
#Various T1 and T2 times (in nanoseconds)
T1_list, T2_list = np.logspace(4,5.5,6), np.logspace(4,5.5,6)*0.66

#Perform simulation for various combinations of noise
fidelities = {}
fidelities["Pure dephasing"] = [compute_T1_T2_effect(T2 = T2).real for T2 in T2_list]
fidelities["Amplitude damping"] = [compute_T1_T2_effect(T1 = T1).real for T1 in T1_list]
fidelities["Both"] = [compute_T1_T2_effect(T1 = T1, T2 = T2).real for T1, T2 in zip(T1_list,T2_list) ]

Influence of coherence times on fidelity¶

In [13]:
for noise_type in fidelities.keys():
    plt.plot(np.array(T2_list)/1000.,fidelities[noise_type],'-o', label = noise_type)
plt.arrow(40, 0.6, 0., 0.1, head_width=20, head_length=0.05, fc='k', ec='k')
plt.text(40,0.55, "IBM QX4 hardware")
plt.xlabel(r"$T_2$ ($T_2 = 0.86 T_1$) [$\mu s$]");
plt.ylim(0.4,1);plt.ylabel("Fidelity");plt.grid()
plt.legend(fancybox=True, loc="lower right");
No description has been provided for this image
In [ ]: