Optimizarea circuitelor cuantice
Toshinari Itoko (21 iunie 2024)
Descarcă PDF-ul al cursului original. Reține că unele fragmente de cod pot deveni deprecate, deoarece acestea sunt imagini statice.
Timpul aproximativ de QPU pentru rularea acestui experiment este de 15 s.
(Notă: Unele celule din partea 2 sunt copiate din notebook-ul „Qiskit Deep dive", scris de Matthew Treinish (menținător Qiskit))
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-aer qiskit-ibm-runtime
# !pip install 'qiskit[visualization]'
# !pip install qiskit_ibm_runtime qiskit_aer
# !pip install jupyter
# !pip install matplotlib pylatexenc pydot pillow
import qiskit
qiskit.__version__
'2.0.2'
import qiskit_ibm_runtime
qiskit_ibm_runtime.__version__
'0.40.1'
import qiskit_aer
qiskit_aer.__version__
'0.17.1'
1. Introducere
Această lecție abordează mai multe aspecte ale optimizării circuitelor în domeniul calculului cuantic. Vom vedea în concret valoarea optimizării circuitelor folosind setările de optimizare integrate în Qiskit. Apoi vom aprofunda subiectul și vom descoperi ce poți face, în calitate de expert în domeniul tău specific de aplicație, pentru a construi circuite într-un mod inteligent. În final, vom analiza îndeaproape ce se întâmplă în timpul transpilării, ceea ce ne ajută să ne optimizăm circuitele.
2. Optimizarea circuitelor contează
Mai întâi comparăm rezultatele rulării circuitelor de pregătire a stării GHZ cu 5 qubiți () cu și fără optimizare.
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.primitives import BackendSamplerV2 as Sampler
from qiskit_ibm_runtime.fake_provider import FakeBrisbane
backend = FakeBrisbane()
Mai întâi folosim un Circuit GHZ sintetizat naiv, după cum urmează.
num_qubits = 5
ghz_circ = QuantumCircuit(num_qubits)
ghz_circ.h(0)
[ghz_circ.cx(0, i) for i in range(1, num_qubits)]
ghz_circ.measure_all()
ghz_circ.draw("mpl")
2.1 Nivelul de optimizare
Există 4 valori disponibile pentru optimization_level, de la 0 la 3. Cu cât nivelul de optimizare este mai mare, cu atât se depune mai mult efort de calcul pentru a optimiza circuitul. Nivelul 0 nu efectuează nicio optimizare și face doar minimul necesar pentru ca circuitul să poată rula pe Backend-ul selectat. Nivelul 3 depune cel mai mare efort (și, de obicei, necesită cel mai mult timp de rulare) pentru a încerca să optimizeze circuitul. Nivelul 1 este nivelul de optimizare implicit.
Transpilăm circuitul fără optimizare (optimization_level=0) și cu optimizare (optimization_level=2).
Observăm o diferență mare în lungimea circuitelor transpilate.
pm0 = generate_preset_pass_manager(
optimization_level=0, backend=backend, seed_transpiler=777
)
pm2 = generate_preset_pass_manager(
optimization_level=2, backend=backend, seed_transpiler=777
)
circ0 = pm0.run(ghz_circ)
circ2 = pm2.run(ghz_circ)
print("optimization_level=0:")
display(circ0.draw("mpl", idle_wires=False, fold=-1))
print("optimization_level=2:")
display(circ2.draw("mpl", idle_wires=False, fold=-1))
optimization_level=0:

optimization_level=2:
2.2 Exercițiu
Încearcă și optimization_level=1 și compară circuitul rezultat cu cele de mai sus. Fă asta modificând codul de mai sus.
Soluție:
pm1 = generate_preset_pass_manager(
optimization_level=1, backend=backend, seed_transpiler=777
)
circ1 = pm1.run(ghz_circ)
print("optimization_level=1:")
display(circ1.draw("mpl", idle_wires=False, fold=-1))
optimization_level=1:
Rulează pe un Backend simulat (simulare cu zgomot). Vezi Anexa 1 pentru cum să rulezi pe un Backend real.
# run the circuits on the fake backend (noisy simulator)
sampler = Sampler(backend=backend)
job = sampler.run([circ0, circ2], shots=10000)
print(f"Job ID: {job.job_id()}")
Job ID: 93a4ac70-e3ea-44ad-aea9-5045840c9076
# get results
result = job.result()
unoptimized_result = result[0].data.meas.get_counts()
optimized_result = result[1].data.meas.get_counts()
from qiskit.visualization import plot_histogram
# plot
sim_result = {"0" * 5: 0.5, "1" * 5: 0.5}
plot_histogram(
[result for result in [sim_result, unoptimized_result, optimized_result]],
bar_labels=False,
legend=[
"ideal",
"no optimization",
"with optimization",
],
)
3. Sinteza circuitelor contează
Comparăm în continuare rezultatele rulării a două circuite de pregătire a stării GHZ cu 5 qubiți () sintetizate diferit.
# Original GHZ circuit (naive synthesis)
ghz_circ.draw("mpl")
# A cleverly-synthesized GHZ circuit
ghz_circ2 = QuantumCircuit(5)
ghz_circ2.h(2)
ghz_circ2.cx(2, 1)
ghz_circ2.cx(2, 3)
ghz_circ2.cx(1, 0)
ghz_circ2.cx(3, 4)
ghz_circ2.measure_all()
ghz_circ2.draw("mpl")
# transpile both with the same optimization level 2
circ_org = pm2.run(ghz_circ)
circ_new = pm2.run(ghz_circ2)
print("original synthesis:")
display(circ_org.draw("mpl", idle_wires=False, fold=-1))
print("new synthesis:")
display(circ_new.draw("mpl", idle_wires=False, fold=-1))
original synthesis:
new synthesis:
Noua sinteză produce un Circuit mai puțin adânc. De ce?
Aceasta se datorează faptului că noul Circuit poate fi mapat pe qubiți conectați liniar, deci și pe graful de cuplare „heavy-hexagon" al IBM® Brisbane, în timp ce circuitul original necesită conectivitate de tip stea (un nod de grad 4) și, prin urmare, nu poate fi mapat pe graful de cuplare heavy-hex, care are noduri de cel mult gradul 3. Ca urmare, circuitul original necesită rutarea qubiților, ceea ce adaugă porți SWAP și mărește numărul de porți.
Ceea ce am făcut în noul Circuit poate fi privit ca o sinteză de Circuit „conștientă de constrângerile de cuplare" realizată manual. Cu alte cuvinte: rezolvarea manuală simultană a sintezei circuitului și a mapării circuitului.
# run the circuits
sampler = Sampler(backend=backend)
job = sampler.run([circ_org, circ_new], shots=10000)
print(f"Job ID: {job.job_id()}")
Job ID: 19d635b0-4d8b-44c2-a76e-49e4b9078b1b
# get results
result = job.result()
synthesis_org_result = result[0].data.meas.get_counts()
synthesis_new_result = result[1].data.meas.get_counts()
# plot
sim_result = {"0" * 5: 0.5, "1" * 5: 0.5}
plot_histogram(
[
result
for result in [
sim_result,
unoptimized_result,
synthesis_org_result,
synthesis_new_result,
]
],
bar_labels=False,
legend=[
"ideal",
"no optimization",
"synthesis_org",
"synthesis_new",
],
)
În general, sinteza circuitelor depinde de aplicație și este prea dificil pentru un software să acopere toate aplicațiile posibile. Transpiler-ul Qiskit nu dispune de funcții pentru sinteza circuitului de pregătire a stării GHZ. Într-un astfel de caz, sinteza manuală a circuitului, cum am arătat mai sus, merită luată în considerare. În această secțiune, analizăm în detaliu cum funcționează transpiler-ul Qiskit, folosind exemplul de Circuit toy de mai jos.
# Build a toy example circuit
from math import pi
import itertools
from qiskit.circuit import QuantumCircuit
from qiskit.circuit.library import excitation_preserving
circuit = QuantumCircuit(4, name="Example circuit")
circuit.append(excitation_preserving(4, reps=1, flatten=True), range(4))
circuit.measure_all()
value_cycle = itertools.cycle([0, pi / 4, pi / 2, 3 * pi / 4, pi, 2 * pi])
circuit.assign_parameters(
[x[1] for x in zip(range(len(circuit.parameters)), value_cycle)], inplace=True
)
circuit.draw("mpl")
3.1 Desenează întregul flux de transpilare Qiskit
Analizăm etapele transpiler-ului (sarcinile) pentru optimization_level=1.
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
# There is no need to read this entire image, but this outputs all the steps in the transpile() call
# for optimization level 1
pm = generate_preset_pass_manager(1, backend, seed_transpiler=42)
pm.draw()

Fluxul este compus din șase etape:
print(pm.stages)
('init', 'layout', 'routing', 'translation', 'optimization', 'scheduling')
3.2 Desenează o etapă individuală
Mai întâi, să desenăm toate sarcinile (etapele transpiler-ului) din etapa init.
pm.init.draw()

Putem rula fiecare etapă individuală. Să rulăm etapa init pentru circuitul nostru. Prin activarea loggerului, putem vedea detaliile rulării.
import logging
logger = logging.getLogger()
logger.setLevel("INFO")
init_out = pm.init.run(circuit)
init_out.draw("mpl", fold=-1)
INFO:qiskit.passmanager.base_tasks:Pass: UnitarySynthesis - 0.03576 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: HighLevelSynthesis - 0.16618 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: BasisTranslator - 0.07176 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: InverseCancellation - 0.27299 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: ContractIdleWiresInControlFlow - 0.00811 (ms)
3.3 Exercițiu
Desenează etapele layout și rulează etapa pentru circuitul de ieșire al etapei init (init_out), modificând celulele folosite mai sus.
Soluție:
display(pm.layout.draw())
layout_out = pm.layout.run(init_out)
layout_out.draw("mpl", idle_wires=False, fold=-1)

INFO:qiskit.passmanager.base_tasks:Pass: SetLayout - 0.01001 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: TrivialLayout - 0.07129 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: CheckMap - 0.08917 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: VF2Layout - 1.24431 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: BarrierBeforeFinalMeasurements - 0.02599 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: SabreLayout - 5.11169 (ms)
Fă același lucru pentru etapa translation.
Soluție:
display(pm.translation.draw())
basis_out = pm.translation.run(layout_out)
basis_out.draw("mpl", idle_wires=False, fold=-1)

INFO:qiskit.passmanager.base_tasks:Pass: UnitarySynthesis - 0.03386 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: HighLevelSynthesis - 0.02718 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: BasisTranslator - 2.64192 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: CheckGateDirection - 0.02217 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: GateDirection - 0.36502 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: BasisTranslator - 0.64778 (ms)

Notă: Nicio etapă individuală nu poate fi rulată întotdeauna independent (deoarece unele necesită transmiterea informațiilor de la o etapă anterioară).
3.4 Etapa de optimizare
Ultima etapă implicită din pipeline este optimizarea. După ce am mapat circuitul pentru țintă, circuitul s-a extins considerabil. Cea mai mare parte a acestei extinderi se datorează ineficiențelor din relațiile de echivalență ale traducerii bazei și ale inserției de swap. Etapa de optimizare este utilizată pentru a reduce dimensiunea și adâncimea circuitului. Rulează o serie de etape într-o buclă do while până când ajunge la o ieșire stabilă.
# pm.pre_optimization.draw()
pm.optimization.draw()

logger = logging.getLogger()
logger.setLevel("INFO")
opt_out = pm.optimization.run(basis_out)
INFO:qiskit.passmanager.base_tasks:Pass: Depth - 0.30112 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: FixedPoint - 0.03195 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: Size - 0.01216 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: FixedPoint - 0.01001 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: Optimize1qGatesDecomposition - 0.63729 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: InverseCancellation - 0.41723 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: ContractIdleWiresInControlFlow - 0.01192 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: GatesInBasis - 0.05484 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: Depth - 0.08583 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: FixedPoint - 0.20599 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: Size - 0.00787 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: FixedPoint - 0.00715 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: Optimize1qGatesDecomposition - 0.16809 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: InverseCancellation - 0.17190 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: ContractIdleWiresInControlFlow - 0.00691 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: GatesInBasis - 0.02408 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: Depth - 0.04935 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: FixedPoint - 0.00525 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: Size - 0.00620 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: FixedPoint - 0.00286 (ms)
opt_out.draw("mpl", idle_wires=False, fold=-1)

4. Exemple aprofundate
4.1 Optimizarea blocurilor cu doi qubiți folosind sinteza unitară cu doi qubiți
Pentru nivelurile 2 și 3, avem mai multe etape (Collect2qBlocks, ConsolidateBlocks, UnitarySynthesis) pentru o optimizare suplimentară, și anume optimizarea blocurilor cu doi qubiți. (Compară fluxul etapei de optimizare pentru nivelul 2 cu cel de mai sus pentru nivelul 1)
Optimizarea blocurilor cu doi qubiți este compusă din doi pași: colectarea și consolidarea blocurilor de 2 qubiți și sinteza matricelor unitare cu 2 qubiți.
pm2 = generate_preset_pass_manager(2, backend, seed_transpiler=42)
pm2.optimization.draw()

from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import (
Collect2qBlocks,
ConsolidateBlocks,
UnitarySynthesis,
)
# Collect 2q blocks and consolidate to unitary when we expect that we can reduce the 2q gate count for that unitary
consolidate_pm = PassManager(
[
Collect2qBlocks(),
ConsolidateBlocks(target=backend.target),
]
)
display(basis_out.draw("mpl", idle_wires=False, fold=-1))
consolidated = consolidate_pm.run(basis_out)
consolidated.draw("mpl", idle_wires=False, fold=-1)

# Synthesize unitaries
UnitarySynthesis(target=backend.target)(consolidated).draw(
"mpl", idle_wires=False, fold=-1
)

logger.setLevel("WARNING")
Am văzut în Partea 2 că fluxul real al compilatorului cuantic nu este atât de simplu și este compus din mai multe etape (sarcini). Aceasta se datorează în principal cerințelor de inginerie software necesare pentru a asigura performanța pentru o gamă largă de circuite de aplicații și mentenabilitatea software-ului. Transpiler-ul Qiskit funcționează bine în majoritatea cazurilor, dar dacă se întâmplă să observi că circuitul tău nu este bine optimizat de transpiler-ul Qiskit, ar fi o oportunitate bună să cercetezi propria optimizare a circuitului specifică aplicației tale, cum am arătat în Partea 1. Tehnologia transpiler-ului evoluează, contribuția ta de cercetare-dezvoltare este binevenită.
from qiskit.circuit import QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler
service = QiskitRuntimeService()
backend = service.backend("ibm_brisbane")
sampler = Sampler(backend)
circ = QuantumCircuit(3)
circ.ccx(0, 1, 2)
circ.measure_all()
circ.draw("mpl")
sampler.run([circ]) # IBMInputValueError will be raised
4.2 Optimizarea circuitelor contează
Mai întâi comparăm rezultatele rulării circuitelor de pregătire a stării GHZ cu 5 qubiți () cu și fără optimizare.
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler
service = QiskitRuntimeService()
# backend = service.backend('ibm_brisbane')
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
) # Eagle
backend
Mai întâi folosim un Circuit GHZ sintetizat naiv, după cum urmează.
num_qubits = 5
ghz_circ = QuantumCircuit(num_qubits)
ghz_circ.h(0)
[ghz_circ.cx(0, i) for i in range(1, num_qubits)]
ghz_circ.measure_all()
ghz_circ.draw("mpl")
Transpilăm circuitul fără optimizare (optimization_level=0) și cu optimizare (optimization_level=2).
După cum poți vedea, există o diferență mare în lungimea circuitelor transpilate.
pm0 = generate_preset_pass_manager(
optimization_level=0, backend=backend, seed_transpiler=777
)
pm2 = generate_preset_pass_manager(
optimization_level=2, backend=backend, seed_transpiler=777
)
circ0 = pm0.run(ghz_circ)
circ2 = pm2.run(ghz_circ)
print("optimization_level=0:")
display(circ0.draw("mpl", idle_wires=False, fold=-1))
print("optimization_level=2:")
display(circ2.draw("mpl", idle_wires=False, fold=-1))
optimization_level=0:

optimization_level=2:
# run the circuits
sampler = Sampler(backend)
job = sampler.run([circ0, circ2], shots=10000)
job_id = job.job_id()
print(f"Job ID: {job_id}")
Job ID: d13rnnemya70008ek1zg
# REPLACE WITH YOUR OWN JOB IDS
job = service.job(job_id)
# get results
result = job.result()
unoptimized_result = result[0].data.meas.get_counts()
optimized_result = result[1].data.meas.get_counts()
from qiskit.visualization import plot_histogram
# plot
sim_result = {"0" * 5: 0.5, "1" * 5: 0.5}
plot_histogram(
[result for result in [sim_result, unoptimized_result, optimized_result]],
bar_labels=False,
legend=[
"ideal",
"no optimization",
"with optimization",
],
)
4.3 Sinteza circuitelor contează
Comparăm în continuare rezultatele rulării a două circuite de pregătire a stării GHZ cu 5 qubiți () sintetizate diferit.
# Original GHZ circuit (naive synthesis)
ghz_circ.draw("mpl")
# A better GHZ circuit (smarter synthesis), you learned in a previous lecture
ghz_circ2 = QuantumCircuit(5)
ghz_circ2.h(2)
ghz_circ2.cx(2, 1)
ghz_circ2.cx(2, 3)
ghz_circ2.cx(1, 0)
ghz_circ2.cx(3, 4)
ghz_circ2.measure_all()
ghz_circ2.draw("mpl")
circ_org = pm2.run(ghz_circ)
circ_new = pm2.run(ghz_circ2)
print("original synthesis:")
display(circ_org.draw("mpl", idle_wires=False, fold=-1))
print("new synthesis:")
display(circ_new.draw("mpl", idle_wires=False, fold=-1))
original synthesis:
new synthesis:
# run the circuits
sampler = Sampler(backend)
job = sampler.run([circ_org, circ_new], shots=10000)
job_id = job.job_id()
print(f"Job ID: {job_id}")
Job ID: d13rp283grvg008j12fg
# REPLACE WITH YOUR OWN JOB IDS
job = service.job(job_id)
# get results
result = job.result()
synthesis_org_result = result[0].data.meas.get_counts()
synthesis_new_result = result[1].data.meas.get_counts()
# plot
sim_result = {"0" * 5: 0.5, "1" * 5: 0.5}
plot_histogram(
[result for result in [sim_result, synthesis_org_result, synthesis_new_result]],
bar_labels=False,
legend=[
"ideal",
"synthesis_org",
"synthesis_new",
],
)
4.4 Descompunerea generală a porții cu 1 qubit
from qiskit import QuantumCircuit, transpile
from qiskit.circuit import Parameter
from qiskit.circuit.library.standard_gates import UGate
phi, theta, lam = Parameter("φ"), Parameter("θ"), Parameter("λ")
qc = QuantumCircuit(1)
qc.append(UGate(theta, phi, lam), [0])
qc.draw(output="mpl")
transpile(qc, basis_gates=["rz", "sx"]).draw(output="mpl")
4.5 Optimizarea blocului cu un qubit
from qiskit import QuantumCircuit
qc = QuantumCircuit(1)
qc.x(0)
qc.y(0)
qc.z(0)
qc.rx(1.23, 0)
qc.ry(1.23, 0)
qc.rz(1.23, 0)
qc.h(0)
qc.s(0)
qc.t(0)
qc.sx(0)
qc.sdg(0)
qc.tdg(0)
qc.draw(output="mpl")
from qiskit.quantum_info import Operator
Operator(qc)
Operator([[ 0.45292511-0.57266982j, -0.66852684-0.14135058j],
[ 0.14135058+0.66852684j, -0.57266982+0.45292511j]],
input_dims=(2,), output_dims=(2,))
from qiskit import transpile
qc_opt = transpile(qc, basis_gates=["rz", "sx"])
qc_opt.draw(output="mpl")
Operator(qc_opt)
Operator([[ 0.45292511-0.57266982j, -0.66852684-0.14135058j],
[ 0.14135058+0.66852684j, -0.57266982+0.45292511j]],
input_dims=(2,), output_dims=(2,))
Operator(qc).equiv(Operator(qc_opt))
True
4.6 Descompunerea Toffoli
qc = QuantumCircuit(3)
qc.ccx(0, 1, 2)
qc.draw(output="mpl")
from qiskit import QuantumCircuit, transpile
qc = QuantumCircuit(3)
qc.ccx(0, 1, 2)
qc = transpile(qc, basis_gates=["rz", "sx", "cx"])
qc.draw(output="mpl")
4.7 Descompunerea porții CU
from qiskit.circuit.library.standard_gates import CUGate
phi, theta, lam, gamma = Parameter("φ"), Parameter("θ"), Parameter("λ"), Parameter("γ")
qc = QuantumCircuit(2)
# qc.cu(theta, phi, lam, gamma, 0, 1)
qc.append(CUGate(theta, phi, lam, gamma), [0, 1])
qc.draw(output="mpl")
from qiskit.circuit.library.standard_gates import CUGate
phi, theta, lam, gamma = Parameter("φ"), Parameter("θ"), Parameter("λ"), Parameter("γ")
qc = QuantumCircuit(2)
qc.append(CUGate(theta, phi, lam, gamma), [0, 1])
qc = transpile(qc, basis_gates=["rz", "sx", "cx"])
qc.draw(output="mpl")
4.8 CX, ECR, CZ sunt echivalente până la Clifford-uri locale
Reține că (Hadamard), ( rotație Z), ( rotație Z), (Pauli X) sunt toate porți Clifford.
qc = QuantumCircuit(2)
qc.cx(0, 1)
qc.draw(output="mpl", style="bw")
qc = QuantumCircuit(2)
qc.cx(0, 1)
transpile(qc, basis_gates=["x", "s", "h", "sdg", "ecr"]).draw(output="mpl", style="bw")
qc = QuantumCircuit(2)
qc.cx(0, 1)
transpile(qc, basis_gates=["h", "cz"]).draw(output="mpl", style="bw")
Folosind porțile de bază cu 1 qubit ale Backend-ului IBM: „rz", „sx" și „x".
qc = QuantumCircuit(2)
qc.cx(0, 1)
transpile(qc, basis_gates=["rz", "sx", "x", "ecr"]).draw(output="mpl", style="bw")
qc = QuantumCircuit(2)
qc.cx(0, 1)
transpile(qc, basis_gates=["rz", "sx", "x", "cz"]).draw(output="mpl", style="bw")
# Check Qiskit version
import qiskit
qiskit.__version__
'2.0.2'