Compară setările Transpiler-ului
Versiuni de pachete
Codul de pe această pagină a fost dezvoltat folosind următoarele cerințe. Recomandăm utilizarea acestor versiuni sau a unora mai noi.
qiskit[all]~=2.4.0
qiskit-ibm-runtime~=0.46.1
Diferite setări ale Transpiler-ului oferă diferite tipuri de optimizare a circuitului, adesea cu prețul unui timp mai lung de procesare clasică. Acest ghid parcurge întregul proces de creare, transpilare și trimitere a circuitelor pentru a demonstra testarea performanței diverselor setări.
Rețineți că aceeași setare ar putea îmbunătăți rezultatele unui circuit, în timp ce îl poate înrăutăți pe altul. Asigurați-vă că inspectați circuitele transpilate rezultate înainte de a le rula pe hardware real.
Configurare și creare a circuitului de test
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
# Create circuit to test transpiler on
from qiskit import QuantumCircuit
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.circuit.library import grover_operator, DiagonalGate
# Use Statevector object to calculate the ideal output
from qiskit.quantum_info import Statevector
from qiskit.visualization import plot_histogram
from qiskit.transpiler import PassManager
from qiskit.circuit.library import XGate
from qiskit.quantum_info import hellinger_fidelity
Creează un circuit mic pe care Transpiler-ul să încerce să îl optimizeze. Acest exemplu creează un circuit care execută algoritmul lui Grover cu un oracol care marchează starea 111. Apoi, simulează distribuția ideală (ce te-ai aștepta să măsori dacă ai rula acest circuit pe un calculator cuantic perfect de un număr infinit de ori) pentru comparație ulterioară.
oracle = DiagonalGate([1] * 7 + [-1])
qc = QuantumCircuit(3)
qc.h([0, 1, 2])
qc = qc.compose(grover_operator(oracle))
qc.draw(output="mpl", style="iqp")
ideal_distribution = Statevector.from_instruction(qc).probabilities_dict()
plot_histogram(ideal_distribution)
Transpilare
Apoi, transpilează circuitele pentru QPU. Vei compara performanța Transpiler-ului cu optimization_level setat la 0 (cel mai scăzut) față de 3 (cel mai ridicat). Nivelul de optimizare cel mai scăzut face minimul necesar pentru a rula Circuit-ul pe dispozitiv: mapează Qubiții circuitului la Qubiții dispozitivului și adaugă Gate-uri swap pentru a permite toate operațiile pe doi Qubiți. Nivelul de optimizare cel mai ridicat este mult mai inteligent și folosește numeroase trucuri pentru a reduce numărul total de poartă-uri. Deoarece Gate-urile multi-Qubit au rate de eroare ridicate și Qubiții decoerează în timp, circuitele mai scurte ar trebui să ofere rezultate mai bune.
Acest exemplu folosește hardware IBM Quantum®, dar îl poți încerca pe orice QPU compatibil cu Qiskit. Rezultatele tale ar putea fi diferite.
Celula următoare transpilează qc pentru ambele valori ale optimization_level, afișează numărul de poartă-uri cu doi Qubiți și adaugă circuitele transpilate într-o listă. Unii algoritmi ai Transpiler-ului sunt randomizați, deci se setează un seed pentru reproductibilitate.
# Use Qiskit Runtime to run jobs on hardware
from qiskit_ibm_runtime import (
QiskitRuntimeService,
SamplerV2 as Sampler,
)
# Select the backend with the fewest number of jobs in the queue
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)
backend.name
'ibm_marrakesh'
# Need to add measurements to the circuit
qc.measure_all()
# Find the correct two-qubit gate
twoQ_gates = set(["ecr", "cz", "cx"])
for gate in backend.basis_gates:
if gate in twoQ_gates:
twoQ_gate = gate
circuits = []
for optimization_level in [0, 3]:
pm = generate_preset_pass_manager(
optimization_level, backend=backend, seed_transpiler=0
)
t_qc = pm.run(qc)
print(
f"Two-qubit gates (optimization_level={optimization_level}): ",
t_qc.count_ops()[twoQ_gate],
)
circuits.append(t_qc)
Two-qubit gates (optimization_level=0): 21
Two-qubit gates (optimization_level=3): 12
Deoarece CNOT-urile au de obicei o rată de eroare ridicată, Circuit-ul transpilat cu optimization_level=3 ar trebui să performeze mult mai bine.
O altă modalitate de a îmbunătăți performanța este prin dynamical decoupling, aplicând o secvență de poartă-uri pe Qubiții inactivi. Aceasta anulează unele interacțiuni nedorite cu mediul. Celula următoare adaugă dynamic decoupling la circuit-ul transpilat cu optimization_level=3 și îl adaugă în listă.
from qiskit_ibm_runtime.transpiler.passes.scheduling import (
ASAPScheduleAnalysis,
PadDynamicalDecoupling,
)
# Get gate durations so the transpiler knows how long each operation takes
durations = backend.target.durations()
# This is the sequence we'll apply to idling qubits
dd_sequence = [XGate(), XGate()]
# Run scheduling and dynamic decoupling passes on circuit
pm = PassManager(
[
ASAPScheduleAnalysis(durations),
PadDynamicalDecoupling(durations, dd_sequence),
]
)
circ_dd = pm.run(circuits[1])
# Add this new circuit to our list
circuits.append(circ_dd)
circ_dd.draw(output="mpl", style="iqp", idle_wires=False)
Execuția circuitului
În acest moment, ai o listă de circuite transpilate cu diferite setări. Apoi, rulează aceste circuite folosind primitiva Sampler și stochează rezultatele în result.
sampler = Sampler(backend)
job = sampler.run(
[(circuit) for circuit in circuits], # sample all three circuits
shots=8000,
)
result = job.result()
Vizualizarea rezultatelor
În final, reprezintă grafic rezultatele de pe dispozitiv față de distribuția ideală. Poți observa că rezultatele cu optimization_level=3 sunt mai aproape de distribuția ideală datorită numărului mai mic de poartă-uri, iar optimization_level=3 + dd este și mai aproape datorită dynamical decoupling.
binary_prob = [
{
k: v / res.data.meas.num_shots
for k, v in res.data.meas.get_counts().items()
}
for res in result
]
plot_histogram(
binary_prob + [ideal_distribution],
bar_labels=False,
legend=[
"optimization_level=0",
"optimization_level=3",
"optimization_level=3 + dd",
"ideal distribution",
],
)
Poți confirma acest lucru calculând fidelitatea Hellinger între fiecare set de rezultate și distribuția ideală (mai mare este mai bine, iar 1 reprezintă fidelitate perfectă).
for prob in binary_prob:
print(f"{hellinger_fidelity(prob, ideal_distribution):.3f}")
0.985
0.989
0.988
Pași următori
-
Explorează câteva resurse avansate de transpilare, cum ar fi:
-
Explorează tutorialele disponibile.