การปรับแต่ง Quantum Circuit
Toshinari Itoko (21 มิถุนายน 2024)
ดาวน์โหลด pdf ของบรรยายต้นฉบับ โปรดทราบว่าโค้ดบางส่วนอาจล้าสมัยเนื่องจากเป็นภาพนิ่ง
เวลา QPU โดยประมาณสำหรับการรันการทดลองนี้คือ 15 วินาที
(หมายเหตุ: เซลล์บางส่วนในพาร์ต 2 คัดลอกมาจาก notebook "Qiskit Deep dive" เขียนโดย Matthew Treinish (Qiskit maintainer))
# 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. บทนำ
บทเรียนนี้จะครอบคลุมหลายแง่มุมของการปรับแต่ง Circuit ในการประมวลผลควอนตัม โดยเฉพาะอย่างยิ่ง เราจะเห็นคุณค่าของการปรับแต่ง Circuit โดยใช้การตั้งค่าการปรับแต่งที่มีใน Qiskit จากนั้นเราจะลงลึกมากขึ้นและดูว่าคุณในฐานะผู้เชี่ยวชาญในสาขาแอปพลิเคชันของตนเองสามารถทำอะไรได้บ้างเพื่อสร้าง Circuit อย่างชาญฉลาด และสุดท้าย เราจะมองอย่างใกล้ชิดว่าเกิดอะไรขึ้นในระหว่างการ Transpile ที่ช่วยให้เราปรับแต่ง Circuit ได้
2. การปรับแต่ง Circuit มีความสำคัญ
เราเปรียบเทียบผลลัพธ์ของการรัน Circuit เตรียมสถานะ GHZ 5-Qubit () แบบมีและไม่มีการปรับแต่ง
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()
เราเริ่มต้นด้วยการใช้ GHZ Circuit ที่สังเคราะห์แบบง่าย ๆ ดังนี้
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 ระดับการปรับแต่ง
มี optimization_level ให้เลือก 4 ระดับตั้งแต่ 0-3 ยิ่งระดับการปรับแต่งสูงเท่าใด ก็จะใช้ความพยายามในการคำนวณมากขึ้นเพื่อปรับแต่ง Circuit ระดับ 0 ไม่ทำการปรับแต่งใด ๆ และทำเพียงขั้นต่ำที่จำเป็นเพื่อให้ Circuit สามารถรันบน Backend ที่เลือกได้ ระดับ 3 ใช้ความพยายาม (และโดยทั่วไปใช้เวลาในการรัน) มากที่สุดเพื่อพยายามปรับแต่ง Circuit ระดับ 1 คือระดับการปรับแต่งเริ่มต้น
เราทำการ Transpile Circuit โดยไม่มีการปรับแต่ง (optimization_level=0) และมีการปรับแต่ง (optimization_level=2)
เราเห็นความแตกต่างอย่างมากในความยาวของ Circuit ที่ผ่านการ Transpile แล้ว
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 แบบฝึกหัด
ลอง optimization_level=1 ด้วย แล้วเปรียบเทียบ Circuit ที่ได้กับสองตัวข้างบน ลองทำโดยแก้ไขโค้ดด้านบน
วิธีแก้:
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:
รันบน fake backend (การจำลองแบบมีสัญญาณรบกวน) ดูภาคผนวก 1 สำหรับวิธีรันบน 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",
],
)
3. การสังเคราะห์ Circuit มีความสำคัญ
ต่อไปเราเปรียบเทียบผลลัพธ์ของการรัน Circuit เตรียมสถานะ GHZ 5-Qubit () สองแบบที่สังเคราะห์ต่างกัน
# 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:
การสังเคราะห์แบบใหม่ให้ Circuit ที่ตื้นกว่า ทำไม?
เพราะ Circuit ใหม่สามารถวางบน Qubit ที่เชื่อมต่อแบบเส้นตรงได้ ดังนั้นบนกราฟการเชื่อมต่อ heavy-hexagon ของ IBM® Brisbane ก็เช่นกัน ในขณะที่ Circuit เดิมต้องการการเชื่อมต่อแบบดาว (โหนดที่มีดีกรี 4) และด้วยเหตุนี้จึงไม่สามารถวางบนกราฟการเชื่อมต่อ heavy-hex ซึ่งมีโหนดที่มีดีกรีมากที่สุด 3 ได้ ผลคือ Circuit เดิมต้องมีการจัดเส้นทาง Qubit ที่เพิ่ม SWAP Gate ทำให้จำนวน Gate เพิ่มขึ้น
สิ่งที่เราทำใน Circuit ใหม่สามารถมองได้ว่าเป็นการสังเคราะห์ Circuit แบบ "คำนึงถึงข้อจำกัดการเชื่อมต่อ" ด้วยตนเอง กล่าวอีกนัยหนึ่งคือ การแก้ปัญหาการสังเคราะห์ Circuit และการแมป Circuit พร้อมกันด้วยตนเอง
# 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",
],
)
โดยทั่วไป การสังเคราะห์ Circuit ขึ้นอยู่กับแอปพลิเคชันและยากเกินไปสำหรับซอฟต์แวร์ที่จะครอบคลุมแอปพลิเคชันที่เป็นไปได้ทั้งหมด Qiskit Transpiler ไม่มีฟังก์ชันสำหรับสังเคราะห์ Circuit เตรียมสถานะ GHZ ในกรณีเช่นนี้ การสังเคราะห์ Circuit ด้วยตนเองตามที่แสดงไว้ข้างต้นเป็นสิ่งที่ควรพิจารณา ในส่วนนี้ เราจะเจาะลึกรายละเอียดว่า Qiskit Transpiler ทำงานอย่างไรโดยใช้ Circuit ตัวอย่างต่อไปนี้
# 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 วาดขั้นตอนการ Transpile ทั้งหมดของ Qiskit
เราดู transpiler passes (งาน) สำหรับ 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()

ขั้นตอนประกอบด้วยหกสเตจ:
print(pm.stages)
('init', 'layout', 'routing', 'translation', 'optimization', 'scheduling')
3.2 วาดสเตจแต่ละอัน
ก่อนอื่น ลองวาดงาน (transpiler passes) ทั้งหมดที่ทำในสเตจ init
pm.init.draw()

เราสามารถรันแต่ละสเตจแยกกันได้ ลองรันสเตจ init สำหรับ Circuit ของเรา โดยเปิดใช้งาน logger เพื่อดูรายละเอียดของการรัน
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 แบบฝึกหัด
วาด passes ของสเตจ layout และรันสเตจสำหรับ Circuit เอาต์พุตของสเตจ init (init_out) โดยแก้ไขเซลล์ที่ใช้ข้างต้น
วิธีแก้:
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)
ทำสิ่งเดียวกันสำหรับสเตจ translation
วิธีแก้:
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)

หมายเหตุ: สเตจแต่ละอันไม่สามารถรันแยกกันได้เสมอไป (เนื่องจากบางสเตจต้องส่งต่อข้อมูลจากสเตจก่อนหน้า)
3.4 สเตจการปรับแต่ง
สเตจเริ่มต้นสุดท้ายในกระบวนการคือการปรับแต่ง หลังจากที่เราฝัง Circuit สำหรับเป้าหมายแล้ว Circuit ได้ขยายตัวออกไปมาก ส่วนใหญ่เป็นเพราะความไม่มีประสิทธิภาพในความสัมพันธ์ความเท่าเทียมจากการแปลฐานและการแทรก SWAP สเตจการปรับแต่งใช้เพื่อพยายามลดขนาดและความลึกของ Circuit โดยรัน passes หลายชุดในลูป do while จนกว่าจะได้เอาต์พุตที่คงที่
# 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. ตัวอย่างเชิงลึก
4.1 การปรับแต่งบล็อก Two-qubit โดยใช้การสังเคราะห์ unitary สองคิวบิต
สำหรับระดับ 2 และ 3 เรามี passes เพิ่มเติม (Collect2qBlocks, ConsolidateBlocks, UnitarySynthesis) สำหรับการปรับแต่งมากขึ้น นั่นคือการปรับแต่งบล็อก Two-qubit (เปรียบเทียบขั้นตอนการปรับแต่งสำหรับระดับ 2 กับระดับ 1 ข้างบน)
การปรับแต่งบล็อก Two-qubit ประกอบด้วยสองขั้นตอน: การรวบรวมและรวมบล็อก 2-qubit และการสังเคราะห์เมทริกซ์ unitary 2-qubit
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")
เราเห็นในพาร์ต 2 ว่าขั้นตอนการคอมไพล์ควอนตัมจริงนั้นไม่ได้เรียบง่ายและประกอบด้วย passes (งาน) หลายอย่าง ส่วนใหญ่เป็นเพราะวิศวกรรมซอฟต์แวร์ที่จำเป็นเพื่อให้มั่นใจในประสิทธิภาพสำหรับ Circuit แอปพลิเคชันที่หลากหลายและความสามารถในการบำรุงรักษาซอฟต์แวร์ Qiskit Transpiler จะทำงานได้ดีในกรณีส่วนใหญ่ แต่ถ้าคุณเห็นว่า Circuit ของคุณไม่ได้รับการปรับแต่งอย่างดีโดย Qiskit Transpiler นั่นอาจเป็นโอกาสที่ดีในการวิจัยการปรับแต่ง Circuit เฉพาะแอปพลิเคชันของคุณเองตามที่แสดงในพาร์ต 1 เทคโนโลยี Transpiler กำลังพัฒนา ยินดีต้อนรับการมีส่วนร่วมด้าน R&D ของคุณ
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 การปรับแต่ง Circuit มีความสำคัญ
เราเปรียบเทียบผลลัพธ์ของการรัน Circuit เตรียมสถานะ GHZ 5-Qubit () แบบมีและไม่มีการปรับแต่ง
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
เราเริ่มต้นด้วยการใช้ GHZ Circuit ที่สังเคราะห์แบบง่าย ๆ ดังนี้
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")
เราทำการ Transpile Circuit โดยไม่มีการปรับแต่ง (optimization_level=0) และมีการปรับแต่ง (optimization_level=2)
ดังที่เห็น มีความแตกต่างอย่างมากในความยาวของ Circuit ที่ผ่านการ Transpile แล้ว
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 การสังเคราะห์ Circuit มีความสำคัญ
ต่อไปเราเปรียบเทียบผลลัพธ์ของการรัน Circuit เตรียมสถานะ GHZ 5-Qubit () สองแบบที่สังเคราะห์ต่างกัน
# 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 การแยกย่อย Gate 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 การปรับแต่งบล็อก One-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 การแยกย่อย 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 การแยกย่อย CU gate
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 เท่ากันจนถึง local Cliffords
โปรดทราบว่า (Hadamard), ( Z-rotation), ( Z-rotation), (Pauli X) ล้วนเป็น Clifford gates
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")
ใช้ basis gates 1q ของ IBM backend คือ "rz", "sx" และ "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'