주 콘텐츠로 건너뛰기

Transpiler 설정 비교

Package versions

The code on this page was developed using the following requirements. We recommend using these versions or newer.

qiskit[all]~=2.4.0
qiskit-ibm-runtime~=0.46.1

다양한 Transpiler 설정은 Circuit에 다양한 유형의 최적화를 제공하며, 종종 더 긴 고전 처리 시간을 수반합니다. 이 가이드는 Circuit을 생성하고, transpile하고, 제출하는 전체 과정을 통해 다양한 설정의 성능을 테스트하는 방법을 안내합니다.

동일한 설정이 한 Circuit의 결과를 개선하면서 다른 Circuit에는 악영향을 줄 수 있다는 점에 유의하세요. 실제 하드웨어에서 실행하기 전에 transpile된 Circuit을 반드시 검토하세요.

설정 및 샘플 Circuit 생성

# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
# Create circuit to test transpiler on
from qiskit import QuantumCircuit
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.circuit.library import grover_operator, DiagonalGate

# Use Statevector object to calculate the ideal output
from qiskit.quantum_info import Statevector
from qiskit.visualization import plot_histogram
from qiskit.transpiler import PassManager

from qiskit.circuit.library import XGate
from qiskit.quantum_info import hellinger_fidelity

Transpiler가 최적화를 시도할 작은 Circuit을 생성합니다. 이 예제에서는 상태 111을 표시하는 오라클을 사용하여 Grover 알고리즘을 수행하는 Circuit을 생성합니다. 다음으로, 나중에 비교하기 위해 이상적인 분포(완벽한 양자 컴퓨터에서 무한히 실행했을 때 측정할 것으로 예상되는 값)를 시뮬레이션합니다.

oracle = DiagonalGate([1] * 7 + [-1])
qc = QuantumCircuit(3)
qc.h([0, 1, 2])
qc = qc.compose(grover_operator(oracle))

qc.draw(output="mpl", style="iqp")

이전 코드 셀의 출력

ideal_distribution = Statevector.from_instruction(qc).probabilities_dict()

plot_histogram(ideal_distribution)

이전 코드 셀의 출력

Transpile

다음으로, QPU를 위해 Circuit을 transpile합니다. optimization_level0(최저)으로 설정한 경우와 3(최고)으로 설정한 경우의 Transpiler 성능을 비교합니다. 최저 최적화 수준은 Circuit을 장치에서 실행하는 데 필요한 최소한의 작업만 수행합니다. 즉, Circuit Qubit을 장치 Qubit에 매핑하고 모든 2-Qubit 연산을 허용하도록 swap Gate를 추가합니다. 최고 최적화 수준은 훨씬 더 스마트하며 전체 Gate 수를 줄이기 위해 다양한 기법을 사용합니다. 다중 qubit Gate는 높은 오류율을 가지고 Qubit은 시간이 지남에 따라 디코히어하기 때문에, 더 짧은 Circuit이 더 나은 결과를 제공합니다.

important

이 예제는 IBM Quantum® 하드웨어를 사용하지만, Qiskit 호환 QPU에서 시도해볼 수 있습니다. 결과는 다를 수 있습니다.

다음 셀은 두 optimization_level 값에 대해 qc를 transpile하고, 2-Qubit Gate 수를 출력하며, transpile된 Circuit을 목록에 추가합니다. Transpiler의 일부 알고리즘은 무작위성을 사용하므로, 재현성을 위해 시드를 설정합니다.

# Use Qiskit Runtime to run jobs on hardware
from qiskit_ibm_runtime import (
QiskitRuntimeService,
SamplerV2 as Sampler,
)
# Select the backend with the fewest number of jobs in the queue
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)
backend.name
'ibm_marrakesh'
# Need to add measurements to the circuit
qc.measure_all()

# Find the correct two-qubit gate
twoQ_gates = set(["ecr", "cz", "cx"])
for gate in backend.basis_gates:
if gate in twoQ_gates:
twoQ_gate = gate

circuits = []
for optimization_level in [0, 3]:
pm = generate_preset_pass_manager(
optimization_level, backend=backend, seed_transpiler=0
)
t_qc = pm.run(qc)
print(
f"Two-qubit gates (optimization_level={optimization_level}): ",
t_qc.count_ops()[twoQ_gate],
)
circuits.append(t_qc)
Two-qubit gates (optimization_level=0): 21
Two-qubit gates (optimization_level=3): 12

CNOT은 일반적으로 높은 오류율을 가지므로, optimization_level=3으로 transpile된 Circuit이 훨씬 더 나은 성능을 보일 것입니다.

성능을 향상시키는 또 다른 방법은 유휴 Qubit에 Gate 시퀀스를 적용하는 동적 디커플링을 통한 것입니다. 이는 환경과의 원치 않는 상호작용 일부를 상쇄합니다. 다음 셀은 optimization_level=3으로 transpile된 Circuit에 동적 디커플링을 추가하고 목록에 추가합니다.

from qiskit_ibm_runtime.transpiler.passes.scheduling import (
ASAPScheduleAnalysis,
PadDynamicalDecoupling,
)

# Get gate durations so the transpiler knows how long each operation takes
durations = backend.target.durations()

# This is the sequence we'll apply to idling qubits
dd_sequence = [XGate(), XGate()]

# Run scheduling and dynamic decoupling passes on circuit
pm = PassManager(
[
ASAPScheduleAnalysis(durations),
PadDynamicalDecoupling(durations, dd_sequence),
]
)
circ_dd = pm.run(circuits[1])

# Add this new circuit to our list
circuits.append(circ_dd)
circ_dd.draw(output="mpl", style="iqp", idle_wires=False)

이전 코드 셀의 출력

Circuit 실행

이 시점에서 다양한 설정으로 transpile된 Circuit 목록이 있습니다. 다음으로, Sampler 프리미티브를 사용하여 이 Circuit을 실행하고 결과를 result에 저장합니다.

sampler = Sampler(backend)
job = sampler.run(
[(circuit) for circuit in circuits], # sample all three circuits
shots=8000,
)
result = job.result()

결과 보기

마지막으로, 장치 실행 결과를 이상적인 분포와 비교하여 플롯합니다. Gate 수가 적기 때문에 optimization_level=3의 결과가 이상적인 분포에 더 가깝고, 동적 디커플링으로 인해 optimization_level=3 + dd는 더욱 가까운 것을 확인할 수 있습니다.

binary_prob = [
{
k: v / res.data.meas.num_shots
for k, v in res.data.meas.get_counts().items()
}
for res in result
]
plot_histogram(
binary_prob + [ideal_distribution],
bar_labels=False,
legend=[
"optimization_level=0",
"optimization_level=3",
"optimization_level=3 + dd",
"ideal distribution",
],
)

이전 코드 셀의 출력

각 결과 세트와 이상적인 분포 사이의 Hellinger 충실도를 계산하여 이를 확인할 수 있습니다(높을수록 좋으며, 1이 완벽한 충실도입니다).

for prob in binary_prob:
print(f"{hellinger_fidelity(prob, ideal_distribution):.3f}")
0.985
0.989
0.988

다음 단계

권장 사항