Sari la conținutul principal

Optimizarea circuitelor cuantice

notă

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 (12(00000+11111)\frac{1}{\sqrt{2}} \left( |00000\rangle + |11111\rangle \right)) 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")

Output of the previous code cell

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:

Output of the previous code cell

optimization_level=2:

Output of the previous code cell

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:

Output of the previous code cell

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",
],
)

Output of the previous code cell

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 (12(00000+11111)\frac{1}{\sqrt{2}} \left( |00000\rangle + |11111\rangle \right)) sintetizate diferit.

# Original GHZ circuit (naive synthesis)
ghz_circ.draw("mpl")

Output of the previous code cell

# 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")

Output of the previous code cell

# 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:

Output of the previous code cell

new synthesis:

Output of the previous code cell

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",
],
)

Output of the previous code cell

Î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")

Output of the previous code cell

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()

Output of the previous code cell

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()

Output of the previous code cell

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)

Output of the previous code cell

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)

Output of the previous code cell

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)

Output of the previous code cell

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)

Output of the previous code cell

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)

Output of the previous code cell

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()

Output of the previous code cell

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)

Output of the previous code cell

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()

Output of the previous code cell

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)

Output of the previous code cell

Output of the previous code cell

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

Output of the previous code cell

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")

Output of the previous code cell

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 (12(00000+11111)\frac{1}{\sqrt{2}} \left( |00000\rangle + |11111\rangle \right)) 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")

Output of the previous code cell

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:

Output of the previous code cell

optimization_level=2:

Output of the previous code cell

# 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",
],
)

Output of the previous code cell

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 (12(00000+11111)\frac{1}{\sqrt{2}} \left( |00000\rangle + |11111\rangle \right)) sintetizate diferit.

# Original GHZ circuit (naive synthesis)
ghz_circ.draw("mpl")

Output of the previous code cell

# 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")

Output of the previous code cell

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:

Output of the previous code cell

new synthesis:

Output of the previous code cell

# 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",
],
)

Output of the previous code cell

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")

Output of the previous code cell

transpile(qc, basis_gates=["rz", "sx"]).draw(output="mpl")

Output of the previous code cell

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")

Output of the previous code cell

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")

Output of the previous code cell

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")

Output of the previous code cell

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")

Output of the previous code cell

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")

Output of the previous code cell

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")

Output of the previous code cell

4.8 CX, ECR, CZ sunt echivalente până la Clifford-uri locale

Reține că HH(Hadamard), SS(π/2\pi/2 rotație Z), SS^\dagger(π/2-\pi/2 rotație Z), XX(Pauli X) sunt toate porți Clifford.

qc = QuantumCircuit(2)
qc.cx(0, 1)
qc.draw(output="mpl", style="bw")

Output of the previous code cell

qc = QuantumCircuit(2)
qc.cx(0, 1)
transpile(qc, basis_gates=["x", "s", "h", "sdg", "ecr"]).draw(output="mpl", style="bw")

Output of the previous code cell

qc = QuantumCircuit(2)
qc.cx(0, 1)
transpile(qc, basis_gates=["h", "cz"]).draw(output="mpl", style="bw")

Output of the previous code cell

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")

Output of the previous code cell

qc = QuantumCircuit(2)
qc.cx(0, 1)
transpile(qc, basis_gates=["rz", "sx", "x", "cz"]).draw(output="mpl", style="bw")

Output of the previous code cell

# Check Qiskit version
import qiskit

qiskit.__version__
'2.0.2'