학습 실험 만들기
이 튜토리얼에서는 표준 학습 실험을 처음부터 만들고, 이를 사용해 시뮬레이션된 노이즈 데이터로부터 모델을 학습하는 방법을 보여 줍니다.
-
Qubit 링에 Gate 집합 정의하기
-
2-local Pauli-Lindblad 모델 선택하기
-
학습 실험 만들기
-
Circuit 생성하기
-
로컬 시뮬레이션 설정하기
-
프로그램 실행하기
-
데이터 분석하기
아래 Circuit은 로컬에서 시뮬레이션되므로, 이 안내를 따라 하는 데 IBM Quantum® 자격 증명이 필요하지 않습니다. 대신 실제 장치에서 실행하려면 다음과 같이 합니다.
-
1단계:
FakeMarrakesh를 실제 Backend로 바꿉니다. -
5단계: 해당 Backend를 executor의 모드로 전달하고, 시뮬레이터 옵션은 제거합니다.
1. Qubit 링에 Gate 집합 정의하기
Backend Target과 그 Qubit의 부분 집합(여기서는 FakeMarrakesh의 12 Qubit 링)으로 QiskitGateSet을 만든 다음, 여기에 Gate를 하나 추가합니다. 이 링을 덮는 CZ Gate 6개로 이루어진 레이어입니다. 기본적으로 Gate 집합은 준비 Gate P와 측정 Gate M으로 초기화됩니다.
from qiskit_ibm_runtime.fake_provider import FakeMarrakesh
from qiskit_noise_learning.gate_sets import QiskitGateSet
backend = FakeMarrakesh()
qubit_subset = [*range(25, 30), *range(37, 39), *range(45, 50)]
cz_pairs = [(25, 26), (27, 28), (29, 38), (37, 45), (46, 47), (48, 49)]
gate_set = QiskitGateSet(backend.num_qubits, target=backend.target, qubit_subset=qubit_subset)
with gate_set.build_new_gate("cz_gate", latex_str=r"\mathrm{CZ}") as builder:
for pair in cz_pairs:
builder.circuit.cz(*pair)
list(gate_set)
['M', 'P', 'cz_gate']
from qiskit_ibm_runtime import QiskitRuntimeService
backend = QiskitRuntimeService().backend("ibm_marrakesh")
draw()를 호출하여 장치 토폴로지 위의 Gate를 확인합니다. Gate 집합에 포함된 Qubit 링과, Gate가 그중 어떤 쌍을 얽히게 하는지 볼 수 있습니다.
gate_set.draw()
2. 2-local Pauli-Lindblad 모델 선택하기
PauliLindbladModel로 노이즈가 가질 수 있는 Pauli-Lindblad 생성자를 결정합니다. k_local()을 사용해 Gate 집합의 연결성으로부터 이 생성자 집합을 만듭니다. 여기서는 유니터리 Gate의 연결된 Qubit 쌍에 대한 모든 2-local Pauli와, 준비 및 측정을 위한 단일 Qubit Pauli를 사용합니다.
from qiskit_noise_learning.models import PauliLindbladModel
model = PauliLindbladModel.k_local(gate_set, gate_k={"cz_gate": 2, "M": 1, "P": 1})
각 Gate의 노이즈 모델에 대한 미지 매개변수 개수를 표시합니다.
{name: len(generators) for name, generators in model.generators.items()}
{'M': 12, 'P': 12, 'cz_gate': 144}
3. 학습 실험 만들기
빌더 단계를 조합하여 Experiment를 구성합니다. 각 단계는 앞선 단계가 기록한 내용을 읽으므로 순서가 중요합니다.
from qiskit_noise_learning.experiment_builder import (
BindFragmentDepths,
CompleteSequences,
EvenDepthVanillaPaths,
Experiment,
GenerateInstructionSequences,
IdentifyRelations,
MergeInstructionSequences,
SPAMPaths,
VanillaInstructionSequences,
)
experiment_builder = (
# Add standard vanilla learning paths and instruction sequences, and identify relations
EvenDepthVanillaPaths()
+ VanillaInstructionSequences()
+ IdentifyRelations()
# Add paths for learning SPAM, then generate and merge instruction sequences for measuring them
+ SPAMPaths()
+ GenerateInstructionSequences()
+ MergeInstructionSequences()
# Finalize by completing the instruction sequences and setting experiment depths
+ CompleteSequences()
+ BindFragmentDepths([2, 16, 32, 64])
)
experiment = experiment_builder.run(
Experiment(fidelity_model=model, shots=20, randomizations=50)
)
print(f"Number of paths: {len(experiment.paths)}")
print(f"Number of instruction sequences: {len(experiment.instruction_sequences)}")
Number of paths: 156
Number of instruction sequences: 37
설계 행렬의 랭크를 확인합니다.
print(f"Design matrix rank: {experiment.design_matrix.rank}")
Design matrix rank: 120
4. Circuit 생성하기
ExecutorCircuitGenerator로 실험을 컴파일하여 QuantumProgram을 생성합니다. 프래그먼트 깊이마다 매개변수화된 템플릿 Circuit이 하나씩 만들어집니다.
from qiskit_noise_learning.circuit_generator import ExecutorCircuitGenerator
circuit_generator = ExecutorCircuitGenerator(gate_set)
quantum_program = circuit_generator.generate(experiment)
print(f"Number of template circuits: {len(quantum_program.items)}")
Number of template circuits: 5
템플릿 Circuit 하나를 그려서 살펴봅니다.
quantum_program.items[0].circuit.draw("mpl", idle_wires=False, fold=False)
5. 로컬 시뮬레이션 설정하기
시뮬레이션용 노이즈 모델을 정의하기 위해, 모델에 이미 있는 생성자에 무작위 비율을 부여하여 PauliLindbladMap을 구성합니다.
import numpy as np
from qiskit.quantum_info import PauliLindbladMap
rng = np.random.default_rng(1234)
true_maps = {
name: PauliLindbladMap.from_terms(
[
PauliLindbladMap.GeneratorTerm(rate, generator)
for generator, rate in zip(generators, rng.uniform(2e-4, 1.5e-3, len(generators)))
]
)
for name, generators in model.generators.items()
if name != "P"
}
{name: noise_map.num_qubits for name, noise_map in true_maps.items()}
{'M': 156, 'cz_gate': 156}
모델이 backend의 Qubit 인덱싱으로 표현되기 때문에 이 맵들의 너비는 장치 전체와 같습니다. 하지만 시뮬레이터는 각 맵의 너비가 적용 대상 Gate의 너비와 같고, Pauli 인덱스가 Gate의 Qubit에 대해 물리적 순서의 오름차순으로 이어지기를 요구합니다. keep_qubits()로 맵을 좁히면 링 밖의 모든 것이 트레이스 아웃됩니다.
noise_dict = {
name: noise_map.keep_qubits(qubit_subset) for name, noise_map in true_maps.items()
}
{name: noise_map.num_qubits for name, noise_map in noise_dict.items()}
{'M': 12, 'cz_gate': 12}
시뮬레이터는 레이어의 노이즈를 해당 레이어를 만든 박스 명령으로 식별하며, 이 박스 명령은 Gate 집합의 임의의 Gate에 대해 to_instruction()이 반환합니다. 각 박스를 해당 맵과 짝지은 뒤 스태빌라이저 시뮬레이션 방법을 사용하고, seed_simulator를 설정하여 시뮬레이션 데이터를 재현 가능하게 만듭니다.
from qiskit_aer import AerSimulator
from qiskit_ibm_runtime import Executor
executor = Executor(
mode=AerSimulator(method="stabilizer"),
options={
"simulator": {
"layer_noise_model": [
(gate_set[name].to_instruction(), noise_map)
for name, noise_map in noise_dict.items()
],
"seed_simulator": 42,
}
},
)
executor = Executor(mode=backend)
6. 프로그램 실행하기
프로그램을 실행한 다음 collect()로 결과를 포맷하여 Fit을 생성합니다. 이는 분석 단계들이 읽고 쓰는 컨테이너입니다.
이 단계는 executor가 시뮬레이터를 가리키든 실제 장치를 가리키든 동일합니다.
job = executor.run(quantum_program)
fit = circuit_generator.collect(job.result())
7. 데이터 분석하기
모델의 비음수 최소제곱 피팅을 중심으로 분석 파이프라인을 구성합니다.
from qiskit_noise_learning.analysis import (
ComputeObservables, # computes observables from raw data
CurveFitObservables, # performs exponential fitting
LeastSquaresSolve, # solves model with (nonnegative) least squares
)
analyzer = ComputeObservables() + CurveFitObservables() + LeastSquaresSolve()
fit = analyzer.run(fit)
/opt/hostedtoolcache/Python/3.15.1/x64/lib/python3.15/site-packages/qiskit_noise_learning/analysis/model_solve.py:250: UserWarning: 7 of 156 row(s) of the linear system have a non-positive or non-finite uncertainty, so they carry no usable statistical weight. Positions in LinearSystemData.row_labels: 144, 145, 148, 149, 151, 152, 154.
system = LinearSystemData.from_fit(fit)
측정된 관측량 평균을 피팅된 지수 함수와 함께 CZ 쌍마다 하나의 서브플롯으로 플롯하여, 피팅 대상인 감쇠가 애초에 지수적인지 확인합니다.
fit.plot_qubit_pair_decays(
pairs=cz_pairs,
observable_type="means",
exponential_fit=True,
)
exponential_fit을 model_prediction으로 바꾸면, 같은 데이터를 비음수 최소제곱 풀이에서 나온 비율이 암시하는 감쇠와 비교할 수 있습니다.
fit.plot_qubit_pair_decays(
pairs=cz_pairs,
observable_type="means",
model_prediction=True,
)
