주 콘텐츠로 건너뛰기

Qiskit Runtime 작업 디버그

패키지 버전

이 페이지의 코드는 다음 요구 사항을 사용하여 개발되었습니다. 이 버전 이상을 사용하길 권장합니다.

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
qiskit-aer~=0.17

리소스를 많이 사용하는 Qiskit Runtime 워크로드를 하드웨어에서 실행하기 위해 제출하기 전에, Qiskit Runtime Neat (Noisy Estimator Analyzer Tool) 클래스를 사용하여 Estimator 워크로드가 올바르게 설정되어 있는지, 정확한 결과를 반환할 가능성이 높은지, 지정된 문제에 가장 적합한 옵션을 사용하는지 등을 확인할 수 있습니다.

Neat는 효율적인 시뮬레이션을 위해 입력 Circuit을 클리퍼드화(Cliffordize)하면서 Circuit의 구조와 깊이를 그대로 유지합니다. 클리퍼드 Circuit은 유사한 수준의 노이즈를 겪으며, 관심 있는 원래 Circuit을 연구하기 위한 좋은 대용품이 됩니다.

다음 예제들은 Neat가 유용한 자원이 될 수 있는 상황을 보여 줍니다. 먼저, 관련 패키지를 가져오고 Qiskit Runtime 서비스에 인증합니다.

환경 준비

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-aer qiskit-ibm-runtime
import numpy as np
import random

from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.quantum_info import SparsePauliOp

from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2 as Estimator
from qiskit_ibm_runtime.debug_tools import Neat

from qiskit_aer.noise import NoiseModel, depolarizing_error
# Choose the least busy backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# Generate a preset pass manager
# This will be used to convert the abstract circuit to an equivalent Instruction Set Architecture (ISA) circuit.
pm = generate_preset_pass_manager(backend=backend, optimization_level=0)

# Set the random seed
random.seed(10)

대상 Circuit 초기화

다음 특성을 가진 6-Qubit Circuit을 고려합니다.

  • 무작위 RZ 회전과 CNOT Gate 레이어를 번갈아 적용합니다.
  • 미러 구조를 가지고 있습니다. 즉, 유니터리 U를 적용한 후 그 역을 적용합니다.
def generate_circuit(n_qubits, n_layers):
r"""
A function to generate a pseudo-random a circuit with ``n_qubits`` qubits and
``2*n_layers`` entangling layers of the type used in this notebook.
"""
# An array of random angles
angles = [
[random.random() for q in range(n_qubits)] for s in range(n_layers)
]

qc = QuantumCircuit(n_qubits)
qubits = list(range(n_qubits))

# do random circuit
for layer in range(n_layers):
# rotations
for q_idx, qubit in enumerate(qubits):
qc.rz(angles[layer][q_idx], qubit)

# cx gates
control_qubits = (
qubits[::2] if layer % 2 == 0 else qubits[1 : n_qubits - 1 : 2]
)
for qubit in control_qubits:
qc.cx(qubit, qubit + 1)

# undo random circuit
for layer in range(n_layers)[::-1]:
# cx gates
control_qubits = (
qubits[::2] if layer % 2 == 0 else qubits[1 : n_qubits - 1 : 2]
)
for qubit in control_qubits:
qc.cx(qubit, qubit + 1)

# rotations
for q_idx, qubit in enumerate(qubits):
qc.rz(-angles[layer][q_idx], qubit)

return qc

# Generate a random circuit
qc = generate_circuit(6, 3)
# Convert the abstract circuit to an equivalent ISA circuit.
isa_qc = pm.run(qc)

qc.draw("mpl", idle_wires=0)

Output of the previous code cell

단일 파울리 Z 연산자를 관측값으로 선택하고, 이를 사용하여 primitive unified bloc(PUB)을 초기화합니다.

# Initialize the observables
obs = ["ZIIIII", "IZIIII", "IIZIII", "IIIZII", "IIIIZI", "IIIIIZ"]
print(f"Observables: {obs}")

# Map the observables to the backend's layout
isa_obs = [SparsePauliOp(o).apply_layout(isa_qc.layout) for o in obs]

# Initialize the PUBs, which consist of six-qubit circuits with `n_layers` 1, ..., 6
all_n_layers = [1, 2, 3, 4, 5, 6]

pubs = [(pm.run(generate_circuit(6, n)), isa_obs) for n in all_n_layers]
Observables: ['ZIIIII', 'IZIIII', 'IIZIII', 'IIIZII', 'IIIIZI', 'IIIIIZ']

Circuit 클리퍼드화

앞서 정의한 PUB Circuit은 클리퍼드가 아니기 때문에 고전적으로 시뮬레이션하기 어렵습니다. 그러나 Neatto_clifford 메서드를 사용하여 더 효율적인 시뮬레이션을 위해 클리퍼드 Circuit으로 매핑할 수 있습니다. to_clifford 메서드는 ConvertISAToClifford Transpiler 패스의 래퍼로, 독립적으로도 사용할 수 있습니다. 특히, 원래 Circuit의 비-클리퍼드 단일-Qubit Gate를 클리퍼드 단일-Qubit Gate로 대체하지만, 2-Qubit Gate, Qubit 수, Circuit 깊이는 변경하지 않습니다.

클리퍼드 Circuit 시뮬레이션에 대한 자세한 내용은 Qiskit Aer 프리미티브를 사용한 안정자 Circuit의 효율적인 시뮬레이션을 참조하세요. 먼저, Neat를 초기화합니다.

# You could specify a custom `NoiseModel` here. If `None`, `Neat`
# pulls the noise model from the given backend
noise_model = None

# Initialize `Neat`
analyzer = Neat(backend, noise_model)

다음으로, PUB를 클리퍼드화합니다.

clifford_pubs = analyzer.to_clifford(pubs)

clifford_pubs[0].circuit.draw("mpl", idle_wires=0)

Output of the previous code cell

애플리케이션 1: Circuit 출력에 대한 노이즈의 영향 분석

이 예제는 Neat를 사용하여 이상적인(ideal_sim) 조건과 노이즈가 있는(noisy_sim) 조건에서 시뮬레이션을 실행함으로써, Circuit 깊이의 함수로서 다양한 노이즈 모델이 PUB에 미치는 영향을 연구하는 방법을 보여줍니다. 이는 QPU에서 작업을 실행하기 전에 실험 결과의 품질에 대한 기대치를 설정하는 데 유용합니다. 노이즈 모델에 대한 자세한 내용은 Qiskit Aer 프리미티브를 이용한 정확한 시뮬레이션 및 노이즈 시뮬레이션을 참조하세요.

시뮬레이션 결과는 수학적 연산을 지원하므로, 서로 비교하거나 (또는 실험 결과와 비교하여) 성능 지표를 계산하는 데 활용할 수 있습니다.

주의

QPU는 다양한 종류의 노이즈에 영향을 받을 수 있습니다. 여기서 사용된 Qiskit Aer 노이즈 모델은 그 중 일부만 시뮬레이션하므로, 실제 QPU의 노이즈보다 덜 심각할 가능성이 높습니다.

QPU에서 노이즈 모델을 초기화할 때 포함되는 오류에 대한 자세한 내용은 Aer NoiseModel API 레퍼런스를 참조하세요.

먼저 이상적인 조건과 노이즈가 있는 조건에서의 고전적 시뮬레이션을 수행합니다.

# Perform a noiseless simulation
ideal_results = analyzer.ideal_sim(clifford_pubs)
print(f"Ideal results:\n {ideal_results}\n")

# Perform a noisy simulation with the backend's noise model
noisy_results = analyzer.noisy_sim(clifford_pubs)
print(f"Noisy results:\n {noisy_results}\n")
Ideal results:
NeatResult([NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.]))])
Noisy results:
NeatResult([NeatPubResult(vals=array([0.99023438, 0.99609375, 0.9921875 , 0.99023438, 0.99414062,
0.99414062])), NeatPubResult(vals=array([0.984375 , 0.99414062, 0.98242188, 0.98828125, 0.98632812,
0.99414062])), NeatPubResult(vals=array([0.96679688, 0.97070312, 0.95898438, 0.97851562, 0.98046875,
0.98828125])), NeatPubResult(vals=array([0.9453125 , 0.953125 , 0.97070312, 0.96875 , 0.98242188,
0.99023438])), NeatPubResult(vals=array([0.93164062, 0.9375 , 0.953125 , 0.96875 , 0.96484375,
0.98046875])), NeatPubResult(vals=array([0.92578125, 0.921875 , 0.93359375, 0.953125 , 0.95898438,
0.9765625 ]))])

다음으로, 수학적 연산을 적용하여 절대 차이를 계산합니다. 이 가이드의 나머지 부분에서는 이상적인 결과와 노이즈 결과 또는 실험 결과를 비교하는 성능 지표로 절대 차이를 사용하지만, 유사한 성능 지표를 설정하는 것도 가능합니다.

절대 차이는 Circuit의 크기가 커질수록 노이즈의 영향도 증가함을 보여줍니다.

# Figure of merit: Absolute difference
def rdiff(res1, re2):
r"""The absolute difference between `res1` and re2`.

--> The closer to `0`, the better.
"""
d = abs(res1 - re2)
return np.round(d.vals * 100, 2)

for idx, (ideal_res, noisy_res) in enumerate(
zip(ideal_results, noisy_results)
):
vals = rdiff(ideal_res, noisy_res)

# Print the mean absolute difference for the observables
mean_vals = np.round(np.mean(vals), 2)
print(
f"Mean absolute difference between ideal and noisy results for circuits with {all_n_layers[idx]} layers:\n {mean_vals}%\n"
)
Mean absolute difference between ideal and noisy results for circuits with 1 layers:
0.72%

Mean absolute difference between ideal and noisy results for circuits with 2 layers:
1.17%

Mean absolute difference between ideal and noisy results for circuits with 3 layers:
2.6%

Mean absolute difference between ideal and noisy results for circuits with 4 layers:
3.16%

Mean absolute difference between ideal and noisy results for circuits with 5 layers:
4.4%

Mean absolute difference between ideal and noisy results for circuits with 6 layers:
5.5%

이러한 유형의 Circuit을 개선하기 위한 대략적이고 단순화된 지침은 다음과 같습니다:

  • 평균 절대 차이가 90%를 초과하는 경우, 오류 완화(mitigation)는 도움이 되지 않을 가능성이 높습니다.
  • 평균 절대 차이가 90% 미만인 경우, 확률적 오류 증폭(PEA)이 결과를 개선할 가능성이 높습니다.
  • 평균 절대 차이가 80% 미만인 경우, Gate 폴딩을 이용한 ZNE도 결과를 개선할 수 있을 가능성이 높습니다.

위의 모든 절대 차이가 90% 미만이므로, 원래 Circuit에 PEA를 적용하면 결과의 품질이 향상될 것으로 기대됩니다. 분석기에서 다양한 노이즈 모델을 지정할 수 있습니다. 다음 예제는 동일한 테스트를 수행하되 사용자 정의 노이즈 모델을 추가합니다.

# Set up a noise model with strength 0.02 on every two-qubit gate
noise_model = NoiseModel()
for qubits in backend.coupling_map:
noise_model.add_quantum_error(
depolarizing_error(0.02, 2), ["ecr", "cx"], qubits
)

# Update the analyzer's noise model
analyzer.noise_model = noise_model

# Perform a noiseless simulation
ideal_results = analyzer.ideal_sim(clifford_pubs)

# Perform a noisy simulation with the backend's noise model
noisy_results = analyzer.noisy_sim(clifford_pubs)

# Compare the results
for idx, (ideal_res, noisy_res) in enumerate(
zip(ideal_results, noisy_results)
):
values = rdiff(ideal_res, noisy_res)

# Print the mean absolute difference for the observables
mean_values = np.round(np.mean(values), 2)
print(
f"Mean absolute difference between ideal and noisy results for circuits with {all_n_layers[idx]} layers:\n {mean_values}%\n"
)
Mean absolute difference between ideal and noisy results for circuits with 1 layers:
0.0%

Mean absolute difference between ideal and noisy results for circuits with 2 layers:
0.0%

Mean absolute difference between ideal and noisy results for circuits with 3 layers:
0.0%

Mean absolute difference between ideal and noisy results for circuits with 4 layers:
0.0%

Mean absolute difference between ideal and noisy results for circuits with 5 layers:
0.0%

Mean absolute difference between ideal and noisy results for circuits with 6 layers:
0.0%

위에서 보이듯이, 노이즈 모델이 주어지면 관심 있는 PUB(의 클리포드화 버전)을 QPU에서 실행하기 전에 노이즈가 미치는 영향을 정량화하려는 시도를 할 수 있습니다.

애플리케이션 2: 다양한 전략 벤치마킹

이 예제는 Neat를 사용하여 PUB에 가장 적합한 옵션을 식별하는 방법을 보여줍니다. 이를 위해 qiskit_aer로 시뮬레이션할 수 없는 PEA를 사용한 추정 문제를 실행하는 것을 고려합니다. Neat를 사용하여 가장 적합한 노이즈 증폭 인수를 결정한 다음, QPU에서 원래 실험을 실행할 때 해당 인수를 사용할 수 있습니다.

# Generate a circuit with six qubits and six layers
isa_qc = pm.run(generate_circuit(6, 3))

# Use the same observables as previously
pubs = [(isa_qc, isa_obs)]
clifford_pubs = analyzer.to_clifford(pubs)
noise_factors = [
[1, 1.1],
[1, 1.1, 1.2],
[1, 1.5, 2],
[1, 1.5, 2, 2.5, 3],
[1, 4],
]
# Run the PUBs on a QPU
estimator = Estimator(backend)
estimator.options.default_shots = 100000
estimator.options.twirling.enable_gates = True
estimator.options.twirling.enable_measure = True
estimator.options.twirling.shots_per_randomization = 100
estimator.options.resilience.measure_mitigation = True
estimator.options.resilience.zne_mitigation = True
estimator.options.resilience.zne.amplifier = "pea"

jobs = []
for factors in noise_factors:
estimator.options.resilience.zne.noise_factors = factors
jobs.append(estimator.run(clifford_pubs))

results = [job.result() for job in jobs]
# Perform a noiseless simulation
ideal_results = analyzer.ideal_sim(clifford_pubs)
# Look at the mean absolute difference to quickly tell the best choice for your options
for factors, res in zip(noise_factors, results):
d = rdiff(ideal_results[0], res[0])
print(
f"Mean absolute difference for factors {factors}:\n {np.round(np.mean(d), 2)}%\n"
)
Mean absolute difference for factors [1, 1.1]:
6.83%

Mean absolute difference for factors [1, 1.1, 1.2]:
8.76%

Mean absolute difference for factors [1, 1.5, 2]:
8.03%

Mean absolute difference for factors [1, 1.5, 2, 2.5, 3]:
10.17%

Mean absolute difference for factors [1, 4]:
8.02%

가장 작은 차이를 보이는 결과가 어떤 옵션을 선택해야 하는지를 제안합니다.

다음 단계

권장 사항