メインコンテンツへスキップ
このプロジェクトはベータ版です。バグを見つけましたか?GitHub Issueを開く。質問やアイデアがありますか?Discussionsで質問する — フィードバックをお待ちしています!
このページの翻訳品質はいかがですか?

学習実験を構築する

このチュートリアルでは、標準的な学習実験をゼロから構築し、それを使ってシミュレートしたノイズ・データからモデルを学習する方法を示します。

  1. 量子ビットのリング上にゲートセットを定義する

  2. 2-local のパウリ・リンドブラッド・モデルを選択する

  3. 学習実験を構築する

  4. 回路を生成する

  5. ローカル・シミュレーションを設定する

  6. プログラムを実行する

  7. データを分析する

実機での実行

以下の回路はローカルでシミュレートされるため、このウォークスルーに IBM Quantum® の認証情報は必要ありません。代わりに実機で実行するには、次のようにします。

  • ステップ 1: FakeMarrakesh を実際のバックエンドに置き換えます。

  • ステップ 5: そのバックエンドをエグゼキューターのモードとして渡し、シミュレーターのオプションを削除します。

1. 量子ビットのリング上にゲートセットを定義する​

バックエンドの Target とその量子ビットの部分集合(ここでは FakeMarrakesh の12量子ビットのリング)から QiskitGateSet を構築し、そこに1つのゲート、つまりそのリングを覆う6個の CZ ゲートの層を追加します。デフォルトでは、ゲートセットは準備ゲート P と測定ゲート 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_set.draw()

2. 2-local のパウリ・リンドブラッド・モデルを選択する​

PauliLindbladModel で、ノイズが持てるパウリ・リンドブラッド生成子を決めます。この生成子の集合は、ゲートセットの接続性から k_local() を使って構築します。ここでは、ユニタリー・ゲートの接続された量子ビットペア上のすべての 2-local パウリと、準備および測定のための単一量子ビット・パウリを使います。

from qiskit_noise_learning.models import PauliLindbladModel

model = PauliLindbladModel.k_local(gate_set, gate_k={"cz_gate": 2, "M": 1, "P": 1})

各ゲートのノイズ・モデルについて、未知パラメーターの数を表示します。

{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. 回路を生成する​

ExecutorCircuitGenerator で実験をコンパイルし、QuantumProgram を生成します。これはフラグメントの深さごとに1つのパラメーター化されたテンプレート回路で構成されます。

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

テンプレート回路を描画して確認します。

quantum_program.items[0].circuit.draw("mpl", idle_wires=False, fold=False)

../_images/4088c7e98baeb5b56925c6c1f276159582c5f7040fbba76de438b919210bcc5f.png

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 の量子ビット・インデックスで表現されているため、これらのマップはデバイス全体の幅を持ちます。しかしシミュレーターは、各マップが適用先のゲートと同じ幅であること、そしてパウリ・インデックスがゲートの量子ビットを物理的な昇順で走ることを求めます。リング外のすべてをトレースアウトする 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}

シミュレーターは、層の元となったボックス命令によってその層のノイズを指定します。このボックス命令は、ゲートセット内の任意のゲートに対して 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 を作成します。これは分析ステージが読み書きするコンテナーです。

このステップは、エグゼキューターがシミュレーターを指している場合でも実機を指している場合でも同じです。

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 ペアごとに1つのサブプロットでプロットし、フィットしている減衰がそもそも指数関数的であるかどうかを確認します。

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