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()
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: 
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();
In [4]:
#reminder:*
print(type(qft_ibm_optimized_circ))
qft_ibm_optimized_circ.display()
<class 'qat.core.wrappers.circuit.Circuit'>
Part II: Noisy simulation¶
Live simulation of noisy simulation on Atos QLM, with simplified hardware model:
- Gates specs: assuming perfect gates
- Environment: noise during qubit idling periods
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)");
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¶
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)
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);
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");
In [ ]:

