周期的境界条件のための回路切断
使用時間の目安イーグルプロセッサーで2分(注:あくまでも目安です。 ランタイムは異なるかもしれない)。
背景
このノートでは、隣接する2つの量子ビット(最初と最後を含む)の間に2つの量子ビットが存在する、量子ビットの周期的なチェーンのシミュレーションを考えます。 周期鎖は、イジングモデルや分子シミュレーションなど、物理学や化学の問題でよく見られる。
現在の IBM Quantum®デバイスは平面型である。 最初と最後の量子ビットが隣接するトポロジー上に、いくつかの周期的な鎖を直接埋め込むことが可能である。 しかし、十分に大きな問題では、最初と最後の量子ビットが離れていることがあり、そのため、これら2つの量子ビット間の2量子ビット演算に多くのSWAPゲートが必要になる。 このような周期境界問題は 本稿 で研究されている。
このノートでは、このようなユーティリティスケールの周期連鎖問題(最初と最後の量子ビットが隣接していない)に対処するための回路切断の使い方を示す。 この長距離接続をカットすることで、回路の複数のインスタンスを実行し、いくつかの古典的な後処理を行う代償として、余分なSWAPゲートを回避することができる。 まとめると、長距離2量子ビット演算を論理的に計算するためにカッティングを取り入れることができる。 言い換えれば、このアプローチはカップリング・マップの接続性を効果的に高めることにつながり、SWAPゲートの数を減らすことになる。
カットには2種類あることに注意してほしい。回路のワイヤーをカットする( wire cutting )場合と、2量子ビットのゲートを複数の1量子ビット演算で置き換える( gate cutting )場合だ。 このノートでは、ゲートカットに焦点を当てる。 ゲートカットの詳細については、 qiskit-addon-cutting の 説明資料 および対応する参考文献を参照のこと。 ワイヤーカットの詳細については、 期待値推定のためのワイヤーカットのチュートリアル、または キスキット・アドン・カット のチュートリアルを参照してください。
要件
このチュートリアルを始める前に、以下のものがインストールされていることを確認してください:
- Qiskit SDK v1.2 またはそれ以降 (
pip install qiskit) - Qiskit Runtime v0.3 またはそれ以降 (
pip install qiskit-ibm-runtime) - 回路切断 Qiskit アドオン v.9.0 以降 (
pip install qiskit-addon-cutting)
セットアップ
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import (
BasisTranslator,
Optimize1qGatesDecomposition,
)
from qiskit.circuit.equivalence_library import (
SessionEquivalenceLibrary as sel,
)
from qiskit.converters import circuit_to_dag, dag_to_circuit
from qiskit.result import sampled_expectation_value
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.circuit.library import TwoLocal
from qiskit_addon_cutting import (
cut_gates,
generate_cutting_experiments,
reconstruct_expectation_values,
)
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2, SamplerOptions, Batchステップ1:古典的な入力を量子問題にマッピングする
ここでは、 TwoLocal 回路を生成し、いくつかのオブザーバブルを定義する。
- 入力回路を作るためのパラメータ
- 出力抽象回路と観測値
entangler map の最後の量子ビットと最初の量子ビットの間に周期的な接続性を持つ TwoLocal 回路について、ハードウェア的に効率的な entangler map を考える。 この長距離相互作用は、トランスパイル時に余分なSWAPゲートを生むことになり、回路の奥行きを広げることになる。
バックエンドと初期レイアウトを選択
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)このノートでは、109量子ビットの周期的な 1D チェーンを考える。これは127量子ビットの IBM 量子デバイスのトポロジーで最も長い 1D チェーンである。 SWAPゲートを追加することなく、127量子ビット・デバイス上に、最初と最後の量子ビットが隣り合うように109量子ビットの周期チェーンを配置することは不可能である。
init_layout = [
13,
12,
11,
10,
9,
8,
7,
6,
5,
4,
3,
2,
1,
0,
14,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32,
36,
51,
50,
49,
48,
47,
46,
45,
44,
43,
42,
41,
40,
39,
38,
37,
52,
56,
57,
58,
59,
60,
61,
62,
63,
64,
65,
66,
67,
68,
69,
70,
74,
89,
88,
87,
86,
85,
84,
83,
82,
81,
80,
79,
78,
77,
76,
75,
90,
94,
95,
96,
97,
98,
99,
100,
101,
102,
103,
104,
105,
106,
107,
108,
112,
126,
125,
124,
123,
122,
121,
120,
119,
118,
117,
116,
115,
114,
113,
]
# the number of qubits in the circuit is governed by the length of the initial layout
num_qubits = len(init_layout)
num_qubitsOutput:
109
TwoLocal 回路のエンタングラーマップを構築する
coupling_map = [(i, i + 1) for i in range(0, len(init_layout) - 1)]
coupling_map.append(
(len(init_layout) - 1, 0)
) # adding in the periodic connectivityTwoLocal 回路により、 rotation_blocks と entangler map を複数回繰り返すことができる。 この場合、繰り返し回数によって、カットする必要のある周期ゲートの数が決まる。 サンプリングのオーバヘッドはカット数に応じて指数関数的に増加するため(詳細は期待値推定のためのワイヤーカットのチュートリアルを参照)、このノートでは繰り返し回数を2に固定する。
num_reps = 2
entangler_map = []
for even_edge in coupling_map[0 : len(coupling_map) : 2]:
entangler_map.append(even_edge)
for odd_edge in coupling_map[1 : len(coupling_map) : 2]:
entangler_map.append(odd_edge)ansatz = TwoLocal(
num_qubits=num_qubits,
rotation_blocks="rx",
entanglement_blocks="cx",
entanglement=entangler_map,
reps=num_reps,
).decompose()
ansatz.draw("mpl", fold=-1)Output:
サーキットカッティングで結果の質を検証するためには、理想的な結果を知る必要がある。 現在選択されている回路は、総当たり的な古典的シミュレーションの域を超えている。 そのため、クリフォードにするために回路のパラメーターを慎重に固定する。
最初の2層の Rx ゲートにはパラメータ値 、最後の層には値 を割り当てることにする。 これにより、この回路の理想的な結果は 、 が量子ビットの数であることが保証される。 したがって、 と の期待値( は量子ビットのインデックス)は、それぞれ と となる。
params_last_layer = [np.pi] * ansatz.num_qubits
params = [0] * (ansatz.num_parameters - ansatz.num_qubits)
params.extend(params_last_layer)
ansatz.assign_parameters(params, inplace=True)観測可能な量を選択する
ゲートカットの利点を定量化するために、観測値 と の期待値を測定する。前述したように、理想的な期待値はそれぞれ と である。
observables = []
for i in range(num_qubits):
obs = "I" * (i) + "Z" + "I" * (num_qubits - i - 1)
observables.append(obs)
for i in range(num_qubits):
if i == num_qubits - 1:
obs = "Z" + "I" * (num_qubits - 2) + "Z"
else:
obs = "I" * i + "ZZ" + "I" * (num_qubits - i - 2)
observables.append(obs)
observables = SparsePauliOp(observables)
paulis = observables.paulis
coeffs = observables.coeffsステップ2:量子ハードウェア実行に向けた問題の最適化
- 入力抽象回路と観測値
- 出力:ターゲット回路と、長距離ゲートの切断によって生成される観測値
回路をトランスパイルする
回路はこの段階でトランスパイルすることも、切断後にトランスパイルすることもできる。 カッティング後にトランスパイルすると、サンプリングのオーバーヘッドが発生するため、生成された各サブ実験をトランスパイルする必要がある。 したがって、この段階でトランスパイルする方が、トランスパイルのオーバーヘッドを減らすことができる。
しかし、この段階でネイティブのハードウェア接続でトランスパイレーションを行うと、トランスパイラーが複数のSWAPゲートを追加して、周期的な2量子ビット演算を配置することになる。 この問題を回避するために、私たちはカットする必要があるゲートを正確に知っていることを活用することができる。 具体的には、このような周期的な2量子ビットゲートに対応するために、遠く離れた量子ビット間に仮想的な接続を追加することで、仮想結合マップを作成することができる。 これにより、余分なSWAPゲートを組み込むことなく、この段階で回路をトランスパイルできるようになる。
coupling_map = backend.configuration().coupling_map
# create a virtual coupling map with long range connectivity
virtual_coupling_map = coupling_map.copy()
virtual_coupling_map.append([init_layout[-1], init_layout[0]])
virtual_coupling_map.append([init_layout[0], init_layout[-1]])pm_virtual = generate_preset_pass_manager(
optimization_level=1,
coupling_map=virtual_coupling_map,
initial_layout=init_layout,
basis_gates=backend.configuration().basis_gates,
)
virtual_mapped_circuit = pm_virtual.run(ansatz)
virtual_mapped_circuit.draw("mpl", fold=-1, idle_wires=False)Output:
長距離の周期的接続性を切断する
次に、トランスパイル回路のゲートをカットする。 カットする必要のある2量子ビットのゲートは、レイアウトの最後と最初の量子ビットを接続するものであることに注意。
# Find the indices of the distant gates
cut_indices = [
i
for i, instruction in enumerate(virtual_mapped_circuit.data)
if {virtual_mapped_circuit.find_bit(q)[0] for q in instruction.qubits}
== {init_layout[-1], init_layout[0]}
]トランスパイルド・サーキットのレイアウトをオブザーバブルに適用する。
trans_observables = observables.apply_layout(virtual_mapped_circuit.layout)最後に、異なる測定ベースと準備ベースにわたるサンプリングによって、サブ実験が生成される。
qpd_circuit, bases = cut_gates(virtual_mapped_circuit, cut_indices)
subexperiments, coefficients = generate_cutting_experiments(
circuits=qpd_circuit,
observables=trans_observables.paulis,
num_samples=np.inf,
)長距離相互作用をカットすることは、測定ベースと準備ベースが異なる回路の複数のサンプルを実行することにつながることに注意してください。 これに関する詳細は、 単一量子ビット操作のサンプリングによる仮想2量子ビットゲートの構築 と 複数の2量子ビット・ユニットを持つ回路の切断 を参照されたい。
カットされる周期ゲートの数は、 TwoLocal 層の繰り返し数に等しく、上記 num_reps のように定義される。 ゲートカットのサンプリングオーバーヘッドは6。 したがって、サブ実験の総数は となる。
print(f"Number of subexperiments is {len(subexperiments)} = 6**{num_reps}")Output:
Number of subexperiments is 36 = 6**2
サブ実験をトランスパイルする
この時点で、サブ実験には、基底ゲートセットに含まれない1量子ビットゲートを持つ回路がいくつか含まれている。 これは、カットされた量子ビットが異なる基底で測定され、これに使用される回転ゲートが必ずしも基底ゲートセットに属さないためである。 例えば、X基底での測定は、通常のZ基底での測定の前にハダマードゲートを適用することを意味する。 しかし、ハダマードはベーシス・ゲートセットの一部ではない。
サブ実験の各回路に全トランスパイル処理を適用する代わりに、特定のトランスパイルパスを使用することができる。 利用可能なすべてのトランスピレーション・パスの詳細については、 本書 を参照してください。
これらの回路のすべてのゲートが基底ゲート集合に属することを保証するために、 BasisTranslator 、次に Optimize1qGatesDecomposition 。 配線や初期レイアウトの選択といった他のステップを再度実行しないため、これら2つのパスを使用する方が、トランスパイルプロセス全体よりも高速である。
pass_ = PassManager(
[Optimize1qGatesDecomposition(basis=backend.configuration().basis_gates)]
)
subexperiments = pass_.run(
[
dag_to_circuit(
BasisTranslator(sel, target_basis=backend.basis_gates).run(
circuit_to_dag(circ)
)
)
for circ in subexperiments
]
)ステップ3: Qiskit primitivesを使用して実行する
- 入力ターゲット回路
- アウトプット準確率分布
カット回路の実行には、 SamplerV2 プリミティブを使用する。 dynamical decoupling 、 twirling 、このタイプの回路ではゲートカットの効果的な適用のみによる改善となる。
options = SamplerOptions()
options.default_shots = 10000
options.dynamical_decoupling.enable = False
options.twirling.enable_gates = False
options.twirling.enable_measure = Falseでは、バッチ・モードを使ってジョブを投入してみよう。
with Batch(backend=backend) as batch:
sampler = SamplerV2(options=options)
cut_job = sampler.run(subexperiments)
print(f"Job ID {cut_job.job_id()}")Output:
Job ID cwxf7wq60bqg008pvt8g
result = cut_job.result()ステップ4:後処理を行い、結果を希望の古典形式で返す
- 入力準確率分布
- 出力再構成された期待値
reconstructed_expvals = reconstruct_expectation_values(
result,
coefficients,
paulis,
)次に、 weight-1 と weight-2 Z型観測値の平均を計算する。
cut_weight_1 = np.mean(reconstructed_expvals[:num_qubits])
cut_weight_2 = np.mean(reconstructed_expvals[num_qubits:])
print(f"Average of weight-1 expectation values is {cut_weight_1}")
print(f"Average of weight-2 expectation values is {cut_weight_2}")Output:
Average of weight-1 expectation values is -0.741733944954063
Average of weight-2 expectation values is 0.6968862385320495
クロス検証:未加工の期待値を取得する
回路切断技術の優位性を切断なしと比較検証することは有益である。 ここでは、回路を切らずに期待値を計算することにする。 このようなカットされていない回路は、最初と最後の量子ビット間の2量子ビット演算を実装するのに必要なSWAPゲートの数が多いことに悩まされることに注意されたい。 SamplerV2 経由で確率分布を求めた後、 sampled_expectation_value 関数を用いてカットされていない回路の期待値を求めることにする。 これにより、すべてのインスタンスでプリミティブを均質に使用することができる。 しかし、 EstimatorV2 、期待値を直接計算することもできた。
if ansatz.num_clbits == 0:
ansatz.measure_all()
pm_uncut = generate_preset_pass_manager(
optimization_level=1, backend=backend, initial_layout=init_layout
)
transpiled_circuit = pm_uncut.run(ansatz)sampler = SamplerV2(mode=backend, options=options)
uncut_job = sampler.run([transpiled_circuit])uncut_job_id = uncut_job.job_id()
print(f"The job id for the uncut clifford circuit is {uncut_job_id}")Output:
The job id for the uncut clifford circuit is cwxfads2ac5g008jhe7g
uncut_result = uncut_job.result()[0]
uncut_counts = uncut_result.data.meas.get_counts()ここで、 weight-1、 weight-2 Z型観測量の平均期待値をカットなしで計算する。
uncut_expvals = [
sampled_expectation_value(uncut_counts, obs) for obs in paulis
]
uncut_weight_1 = np.mean(uncut_expvals[:num_qubits])
uncut_weight_2 = np.mean(uncut_expvals[num_qubits:])
print(f"Average of weight-1 expectation values is {uncut_weight_1}")
print(f"Average of weight-2 expectation values is {uncut_weight_2}")Output:
Average of weight-1 expectation values is -0.32494128440366965
Average of weight-2 expectation values is 0.32340917431192656
可視化
ここで、周期的連鎖回路にゲートカットを使用した場合に、 weight-1 と weight-2 の観測値が改善されることを視覚化してみよう
mpl.rcParams.update(mpl.rcParamsDefault)
fig = plt.subplots(figsize=(12, 8), dpi=200)
width = 0.25
labels = ["Weight-1", "Weight-2"]
x = np.arange(len(labels))
ideal = [-1, 1]
cut = [cut_weight_1, cut_weight_2]
uncut = [uncut_weight_1, uncut_weight_2]
br1 = np.arange(len(ideal))
br2 = [x + width for x in br1]
br3 = [x + width for x in br2]
plt.bar(
br1, ideal, width=width, edgecolor="k", label="Ideal", color="#4589ff"
)
plt.bar(br2, cut, width=width, edgecolor="k", label="Cut", color="#a56eff")
plt.bar(
br3, uncut, width=width, edgecolor="k", label="Uncut", color="#009d9a"
)
plt.axhline(y=0, color="k", linestyle="-")
plt.xticks([r + width for r in range(len(ideal))], labels, fontsize=14)
plt.yticks(fontsize=14)
plt.legend(fontsize=14)
plt.show()Output:
サマリー
要約すると、109量子ビットの周期的な 1D チェーンについて、 weight-1 と weight-2 Z-types観測量の平均期待値を計算した。 そのために
- 1D、最初の量子ビットと最後の量子ビットの間に長距離接続を追加して仮想結合マップを作成し、回路をトランスパイルした。
- この段階でトランスパイルすることで、カット後に各サブ実験を別々にトランスパイルするオーバーヘッドを避けることができた、
- 仮想結合マップを使うことで、最初の量子ビットと最後の量子ビットの間の2量子ビット演算のための余分なSWAPゲートを避けることができた。
- ゲートカットによってトランスパイルド回路から長距離コネクティビティを取り除いた。
- カットされた回路は、適切なトランスピレーション・パスを適用することで、基底ゲート・セットに変換される。
SamplerV2プリミティブを使用して、 IBM 量子デバイス上でカット回路を実行した。- は、カットされた回路の結果を再構成することによって期待値を得た。
推定
この結果から、 weight-1 、 weight-2 タイプの観測値の平均が、周期ゲートをカットすることによって著しく改善されることに気づく。 なお、この研究にはエラー抑制や軽減の技術は含まれていない。 この改善は、この問題に対するゲートカットの適切な使い方によるものである。 緩和と抑制のテクニックを用いれば、結果はさらに改善されただろう。
この研究は、ゲートカットを効果的に使って計算性能を向上させた例を示している。
チュートリアル調査
このチュートリアルに関するご意見・ご感想をお寄せください。 あなたの洞察は、私たちのコンテンツの提供とユーザーエクスペリエンスを向上させるのに役立ちます。