Přeskočit na hlavní obsah

Optimalizace kvantových obvodů

poznámka

Toshinari Itoko (21. června 2024)

Stáhni PDF původní přednášky. Některé úryvky kódu mohou být zastaralé, protože se jedná o statické obrázky.

Přibližná doba QPU pro spuštění tohoto experimentu je 15 s.

(Poznámka: Některé buňky části 2 jsou převzaty z notebooku „Qiskit Deep dive", který napsal Matthew Treinish (správce Qiskitu))

# 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. Úvod

Tato lekce se věnuje několika aspektům optimalizace obvodů v kvantových výpočtech. Konkrétně uvidíme, jakou hodnotu má optimalizace obvodů prostřednictvím nastavení optimalizace zabudovaných v Qiskitu. Poté půjdeme trochu hlouběji a podíváme se, co můžeš jako odborník ve svém konkrétním aplikačním oboru udělat pro chytré sestavování obvodů. Nakonec se podrobně podíváme na to, co se děje během transpilace, což nám pomáhá naše obvody optimalizovat.

2. Na optimalizaci obvodů záleží

Nejprve porovnáme výsledky spuštění přípravných obvodů pro 5-qubitový stav GHZ (12(00000+11111)\frac{1}{\sqrt{2}} \left( |00000\rangle + |11111\rangle \right)) s optimalizací a bez ní.

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

Nejprve použijeme obvod pro stav GHZ naivně syntetizovaný takto.

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

Výstup předchozí buňky kódu

2.1 Úroveň optimalizace

K dispozici jsou 4 úrovně optimization_level v rozsahu 0–3. Čím vyšší je úroveň optimalizace, tím více výpočetního úsilí je vynaloženo na optimalizaci obvodu. Úroveň 0 neprovádí žádnou optimalizaci a pouze vykoná minimální množství práce potřebné ke spuštění obvodu na zvoleném backend. Úroveň 3 vynakládá největší úsilí (a zpravidla i čas) na pokus o optimalizaci obvodu. Úroveň 1 je výchozí úrovní optimalizace. Obvod transpilujeme bez optimalizace (optimization_level=0) a s optimalizací (optimization_level=2). Uvidíme velký rozdíl v délce transpilovaných obvodů.

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:

Výstup předchozí buňky kódu

optimization_level=2:

Výstup předchozí buňky kódu

2.2 Cvičení

Vyzkoušej také optimization_level=1 a porovnej výsledný obvod s předchozími dvěma. Zkus to úpravou výše uvedeného kódu.

Řešení:

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:

Výstup předchozí buňky kódu

Spusť na fiktivním backend (simulace se šumem). Viz Příloha 1 pro spuštění na reálném backend.

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

Výstup předchozí buňky kódu

3. Na syntéze obvodů záleží

Dále porovnáme výsledky spuštění dvou různě syntetizovaných obvodů pro přípravu 5-qubitového GHZ stavu (12(00000+11111)\frac{1}{\sqrt{2}} \left( |00000\rangle + |11111\rangle \right)).

# 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

Nová syntéza produkuje mělčí obvod. Proč?

Je to proto, že nový obvod lze rozložit na lineárně propojené qubity, a tedy i na coupling grafu typu heavy-hexagon procesoru IBM® Brisbane, zatímco původní obvod vyžaduje hvězdicové propojení (uzel stupně 4), a proto jej na coupling grafu heavy-hex nelze rozložit, neboť uzly mají nejvýše stupeň 3. V důsledku toho původní obvod vyžaduje směrování qubitů, které přidává hradla SWAP a zvyšuje celkový počet hradel.

To, co jsme provedli v novém obvodu, lze chápat jako ruční syntézu obvodu „s ohledem na omezení propojení". Jinými slovy: ručně jsme vyřešili syntézu obvodu a mapování obvodu zároveň.

# 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

Obecně platí, že syntéza obvodů závisí na konkrétní aplikaci a je příliš obtížné, aby software pokryl všechny možné aplikace. Qiskit transpiler nemá funkce pro syntézu obvodů pro přípravu GHZ stavu. V takovém případě stojí za zvážení ruční syntéza obvodu, jak je ukázáno výše. V této části se podrobněji podíváme na to, jak Qiskit transpiler funguje, na následujícím jednoduchém příkladovém obvodu.

# 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 Zobrazení celého procesu transpilace Qiskit

Podíváme se na průchody (úlohy) transpileru pro 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

Tok se skládá ze šesti fází:

print(pm.stages)
('init', 'layout', 'routing', 'translation', 'optimization', 'scheduling')

3.2 Zobrazení jednotlivé fáze

Nejprve si zobrazme všechny úlohy (průchody transpileru) provedené ve fázi init.

pm.init.draw()

Výstup předchozí buňky kódu

Každou jednotlivou fázi můžeme spustit samostatně. Spusťme fázi init pro náš obvod. Povolením loggeru uvidíme podrobnosti běhu.

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)

Výstup předchozí buňky kódu

3.3 Cvičení

Zobraz průchody fáze layout a spusť tuto fázi pro výstupní obvod fáze init (init_out), a to úpravou buněk použitých výše.

Řešení:

display(pm.layout.draw())
layout_out = pm.layout.run(init_out)
layout_out.draw("mpl", idle_wires=False, fold=-1)

Výstup předchozí buňky kódu

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)

Výstup předchozí buňky kódu

Totéž proveď pro fázi translation.

Řešení:

display(pm.translation.draw())
basis_out = pm.translation.run(layout_out)
basis_out.draw("mpl", idle_wires=False, fold=-1)

Výstup předchozí buňky kódu

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)

Výstup předchozí buňky kódu

Poznámka: Jednotlivé fáze nelze vždy spouštět nezávisle (některé z nich totiž potřebují přenést informace z předchozí fáze).

3.4 Fáze optimalizace

Poslední výchozí fází v pipeline je optimalizace. Poté, co jsme obvod zabudovali pro cílový hardware, se výrazně rozrostl. Většina tohoto nárůstu je způsobena neefektivnostmi ve vztazích ekvivalence pocházejících z překladu báze a vkládání swapů. Fáze optimalizace se snaží minimalizovat velikost a hloubku obvodu. Spouští sérii průchodů ve smyčce do while, dokud nedosáhne stabilního výstupu.

# pm.pre_optimization.draw()
pm.optimization.draw()

Výstup předchozí buňky kódu

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)

Výstup předchozí buňky kódu

4. Podrobné příklady

4.1 Optimalizace dvouqubitových bloků pomocí syntézy dvouqubitových unitárních matic

Pro úrovně 2 a 3 máme k dispozici další průchody (Collect2qBlocks, ConsolidateBlocks, UnitarySynthesis) pro pokročilejší optimalizaci, konkrétně optimalizaci dvouqubitových bloků. (Porovnej průběh optimalizační fáze pro úroveň 2 s výše uvedeným průběhem pro úroveň 1.)

Optimalizace dvouqubitových bloků se skládá ze dvou kroků: sbírání a konsolidace 2-qubitových bloků a syntéza 2-qubitových unitárních matic.

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

Ve druhé části jsme viděli, že skutečný průběh kvantového kompilátoru není takto jednoduchý a skládá se z mnoha průchodů (úloh). Je to především kvůli softwarovému inženýrství potřebnému k zajištění výkonu pro širokou škálu aplikačních obvodů a udržovatelnosti softwaru. Qiskit transpiler funguje dobře ve většině případů, ale pokud zjistíš, že tvůj obvod není transpilerem Qiskit dobře optimalizován, je to dobrá příležitost zkoumat vlastní optimalizaci obvodů specifickou pro tvoji aplikaci, jak bylo ukázáno v části 1. Technologie transpileru se vyvíjí – tvůj přínos v oblasti výzkumu a vývoje je vítán.

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 Na optimalizaci obvodů záleží

Nejprve porovnáme výsledky spuštění přípravných obvodů pro 5-qubitový stav GHZ (12(00000+11111)\frac{1}{\sqrt{2}} \left( |00000\rangle + |11111\rangle \right)) s optimalizací a bez ní.

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

Nejprve použijeme obvod GHZ naivně syntetizovaný následujícím způsobem.

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

Obvod transpilujeme bez optimalizace (optimization_level=0) a s optimalizací (optimization_level=2). Jak vidíš, délka transpilovaných obvodů se výrazně liší.

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 Syntéza obvodů má vliv

Nyní porovnáme výsledky spuštění dvou různě syntetizovaných obvodů pro přípravu 5-qubitového stavu GHZ (12(00000+11111)\frac{1}{\sqrt{2}} \left( |00000\rangle + |11111\rangle \right)).

# 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 Obecná dekompozice 1-qubitového hradla

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 Optimalizace 1-qubitového bloku

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 Toffoli dekompozice

qc = QuantumCircuit(3)
qc.ccx(0, 1, 2)
qc.draw(output="mpl")

Výstup předchozí buňky kódu

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

Výstup předchozí buňky kódu

4.7 Dekompozice hradla 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")

Výstup předchozí buňky kódu

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

Výstup předchozí buňky kódu

4.8 CX, ECR, CZ jsou si rovny až na lokální Cliffordovy operace

Všimni si, že HH(Hadamard), SS(π/2\pi/2 Z-rotace), SS^\dagger(π/2-\pi/2 Z-rotace) a XX(Pauliho X) jsou všechna Cliffordova hradla.

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

Výstup předchozí buňky kódu

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

Výstup předchozí buňky kódu

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

Výstup předchozí buňky kódu

Použití 1q základních hradel IBM backendu: „rz", „sx" a „x".

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

Výstup předchozí buňky kódu

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

Výstup předchozí buňky kódu

# Check Qiskit version
import qiskit

qiskit.__version__
'2.0.2'