주 콘텐츠로 건너뛰기

양자 회로 최적화

참고

Toshinari Itoko (2024년 6월 21일)

원본 강의의 PDF를 다운로드하세요. 일부 코드 스니펫은 정적 이미지이므로 더 이상 지원되지 않을 수 있습니다.

이 실험의 예상 QPU 실행 시간은 약 15초입니다.

(참고: 파트 2의 일부 셀은 Matthew Treinish(Qiskit 메인테이너)가 작성한 "Qiskit Deep dive" 노트북에서 가져왔습니다.)

# 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. 소개

이 강의에서는 양자 컴퓨팅에서 회로 최적화의 여러 측면을 다룹니다. 구체적으로, Qiskit에 내장된 최적화 설정을 사용하여 회로 최적화의 가치를 살펴봅니다. 그런 다음, 특정 응용 분야의 전문가로서 회로를 스마트하게 구성하는 방법을 더 깊이 살펴봅니다. 마지막으로 트랜스파일레이션 과정에서 회로 최적화에 도움이 되는 내용을 자세히 살펴봅니다.

2. 회로 최적화의 중요성

먼저 최적화 유무에 따라 5-큐비트 GHZ 상태(12(00000+11111)\frac{1}{\sqrt{2}} \left( |00000\rangle + |11111\rangle \right)) 준비 회로의 실행 결과를 비교합니다.

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 회로를 사용합니다.

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은 0에서 3까지 네 가지 옵션이 있습니다. 최적화 레벨이 높을수록 회로 최적화에 더 많은 계산 노력이 소요됩니다. 레벨 0은 최적화를 수행하지 않고 선택한 백엔드에서 회로를 실행할 수 있는 최소한의 작업만 합니다. 레벨 3은 회로를 최적화하기 위해 가장 많은 노력(및 일반적으로 런타임)을 소요합니다. 레벨 1이 기본 최적화 레벨입니다. 최적화 없이(optimization_level=0) 및 최적화와 함께(optimization_level=2) 회로를 트랜스파일합니다. 트랜스파일된 회로의 길이에서 큰 차이를 볼 수 있습니다.

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도 시도해 보고 위의 두 결과와 비교해 보세요. 위의 코드를 수정하여 시도해 보세요.

풀이:

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:

이전 코드 셀의 출력

가짜 백엔드(노이즈 시뮬레이션)에서 실행합니다. 실제 백엔드에서 실행하는 방법은 부록 1을 참조하세요.

# 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. 회로 합성의 중요성

서로 다르게 합성된 두 가지 5-큐비트 GHZ 상태(12(00000+11111)\frac{1}{\sqrt{2}} \left( |00000\rangle + |11111\rangle \right)) 준비 회로의 실행 결과를 비교합니다.

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

이전 코드 셀의 출력

새로운 합성은 더 얕은 회로를 생성합니다. 왜일까요?

이는 새 회로가 선형으로 연결된 큐비트에 배치될 수 있기 때문입니다. IBM® Brisbane의 헤비-헥사곤 커플링 그래프에서도 마찬가지입니다. 반면, 원래 회로는 별형 연결(차수 4 노드)이 필요하며, 따라서 노드가 최대 차수 3인 헤비-헥스 커플링 그래프에 배치될 수 없습니다. 결과적으로 원래 회로는 SWAP 게이트를 추가하는 큐비트 라우팅이 필요하여 게이트 수가 증가합니다.

새 회로에서 수행한 작업은 수동 "커플링 제약 인식" 회로 합성으로 볼 수 있습니다. 즉, 회로 합성과 회로 매핑을 동시에 수동으로 해결한 것입니다.

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

이전 코드 셀의 출력

일반적으로 회로 합성은 응용 분야에 따라 다르며 소프트웨어가 모든 가능한 응용을 다루기는 매우 어렵습니다. Qiskit 트랜스파일러에는 GHZ 상태 준비 회로를 합성하는 기능이 없습니다. 이러한 경우, 위에서 보여준 것과 같은 수동 회로 합성을 고려할 가치가 있습니다. 이 섹션에서는 다음 장난감 예제 회로를 사용하여 Qiskit 트랜스파일러가 어떻게 작동하는지 자세히 살펴봅니다.

# 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 전체 Qiskit 트랜스파일레이션 흐름 그리기

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 개별 단계 그리기

먼저 init 단계에서 수행되는 모든 작업(트랜스파일러 패스)을 그립니다.

pm.init.draw()

이전 코드 셀의 출력

각 개별 단계를 실행할 수 있습니다. 회로에 대해 init 단계를 실행해 봅시다. 로거를 활성화하면 실행의 세부 사항을 볼 수 있습니다.

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 연습 문제

위에서 사용한 셀을 수정하여 layout 단계 패스를 그리고 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 최적화 단계

파이프라인의 마지막 기본 단계는 최적화입니다. 대상에 맞게 회로를 임베딩한 후 회로가 상당히 확장됩니다. 이는 대부분 기저 변환 및 SWAP 삽입에서의 동치 관계의 비효율성 때문입니다. 최적화 단계는 회로의 크기와 깊이를 최소화하려고 합니다. 안정적인 출력에 도달할 때까지 일련의 패스를 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 2-큐비트 유니터리 합성을 사용한 2-큐비트 블록 최적화

레벨 2와 3에서는 추가 최적화를 위해 더 많은 패스(Collect2qBlocks, ConsolidateBlocks, UnitarySynthesis)가 있습니다. 즉, 2-큐비트 블록 최적화입니다. (레벨 1의 최적화 단계 흐름과 위의 레벨 2의 최적화 단계 흐름을 비교해 보세요.)

2-큐비트 블록 최적화는 두 단계로 구성됩니다: 2-큐비트 블록 수집 및 통합, 그리고 2-큐비트 유니터리 행렬 합성.

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에서 실제 양자 컴파일러 흐름은 그렇게 단순하지 않으며 많은 패스(작업)로 구성되어 있음을 보았습니다. 이는 주로 광범위한 응용 회로에 대한 성능 보장과 소프트웨어의 유지보수성을 위한 소프트웨어 엔지니어링 때문입니다. Qiskit 트랜스파일러는 대부분의 경우 잘 작동하지만, 회로가 Qiskit 트랜스파일러에 의해 잘 최적화되지 않은 경우, 파트 1에서 보여준 것과 같이 자체 응용 특화 회로 최적화를 연구할 좋은 기회가 될 것입니다. 트랜스파일러 기술은 진화하고 있으며, 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 회로 최적화의 중요성

최적화 유무에 따라 5-큐비트 GHZ 상태(12(00000+11111)\frac{1}{\sqrt{2}} \left( |00000\rangle + |11111\rangle \right)) 준비 회로의 실행 결과를 비교합니다.

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 회로를 사용합니다.

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

이전 코드 셀의 출력

최적화 없이(optimization_level=0) 및 최적화와 함께(optimization_level=2) 회로를 트랜스파일합니다. 보다시피 트랜스파일된 회로의 길이에서 큰 차이가 있습니다.

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 회로 합성의 중요성

서로 다르게 합성된 두 가지 5-큐비트 GHZ 상태(12(00000+11111)\frac{1}{\sqrt{2}} \left( |00000\rangle + |11111\rangle \right)) 준비 회로의 실행 결과를 비교합니다.

# 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 일반 1-큐비트 게이트 분해

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 1-큐비트 블록 최적화

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 게이트 분해

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는 로컬 Clifford까지 동등함

참고로 HH(Hadamard), SS(π/2\pi/2 Z-회전), SS^\dagger(π/2-\pi/2 Z-회전), XX(Pauli X)는 모두 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")

이전 코드 셀의 출력

IBM 백엔드 1-큐비트 기저 게이트 "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'