期待値推定のためのワイヤー切断
推定実行時間:Heronプロセッサで22秒(注:これはあくまで推定値です。 (実行時間は環境によって異なる場合があります。)
学習成果
このチュートリアルを終えた後、ユーザーは以下の点を理解できるようになります:
- 大きな回路を小さなサブ回路に分割し、それによってノイズの影響を低減する方法
qiskit-addon-cutting
前提条件
このチュートリアルを進める前に、以下のトピックについてあらかじめ理解しておいていただくことをお勧めします:
- このワークフローで使用される「 Sampler 」プリミティブを使用する
背景
回路編みとは、回路を、より少ないゲートや量子ビットで構成される複数の小さなサブ回路に分割するさまざまな手法を総称する用語である。 各サブ回路は独立して実行することができ、最終結果は各サブ回路の出力に対して古典的な後処理を行うことで得られる。 この手法は、 Qiskitの 「Circuit cutting」アドオンで利用可能です。詳細については、 ドキュメントやその他の入門資料をご参照ください。
このチュートリアルでは、配線に沿って回路を分割する 「ワイヤーカッティング 」と呼ばれる手法に焦点を当てています [1], [2]。 なお、従来の回路では、分割点での出力が決定論的に決定でき、0か1のいずれかとなるため、分割は簡単である。 しかし、カットの時点における量子ビットの状態は、一般に混合状態である。 したがって、各サブ回路については、異なる基底(通常はパウリ基底 [3]、[4] のようなトモグラフィー的に完全な基底)を用いて複数回測定を行い、それに応じて固有状態に準備する必要がある。 下図(出典: [7] )は、4量子ビットのGHZ状態を3つのサブ回路に分割するためのワイヤーカットの例を示している。 ここで、 は基底の集合(通常は Pauli X, Y, Z)を表し、 は固有状態の集合(通常は , , , )を表す。

各サブ回路は量子ビットやゲートの数が少ないため、ノイズの影響を受けにくいと予想される。 このチュートリアルでは、この手法を用いてシステムのノイズを効果的に低減できる例を紹介します。
要件
このチュートリアルを始める前に、以下のものがインストールされていることを確認してください:
- Qiskit SDK v2.0 またはそれ以降、 可視化サポート付き
- Qiskit Runtime v0.22 またはそれ以降 (
pip install qiskit-ibm-runtime) - 回路作成用Qiskitアドオン v0.10.0 以降 (
pip install qiskit-addon-cutting) - Qiskit アドオン utils 0.3 以降 (
pip install qiskit-addon-utils) - Qiskit Aer (
pip install qiskit-aer)
セットアップ
import numpy as np
import matplotlib.pyplot as plt
from qiskit.circuit import Parameter, ParameterVector, QuantumCircuit
from qiskit.quantum_info import PauliList, SparsePauliOp
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_aer import AerSimulator
from qiskit.result import sampled_expectation_value
from qiskit_addon_cutting.instructions import CutWire
from qiskit_addon_cutting import (
cut_wires,
expand_observables,
partition_problem,
generate_cutting_experiments,
reconstruct_expectation_values,
)
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2, Batch小規模シミュレータの例
このチュートリアルでは、1次元( 1D )の多体局在(MBL)回路をシミュレートするためのQiskitパターンを実装します。 MBL回路はハードウェア効率に優れた回路であり、 および の2つのパラメータによって定数化されます。 を に設定し、 ですべての量子ビットに対して初期状態を準備した場合、各量子ビットサイト において、 の理想的な期待値は、 の値にかかわらず、 となります。この回路に関する詳細は、こちらの記事をご覧ください。
なお、ノイズのないシミュレータでは、回路を切断した場合としない場合で得られる期待値は同じになります。
ステップ1:古典的な入力を量子問題にマッピングする
1D のMBL回路を組み立てる
まず、 1D のMBL回路を構成するための関数を紹介します。
class MBLChainCircuit(QuantumCircuit):
def __init__(
self, num_qubits: int, depth: int, use_cut: bool = False
) -> None:
super().__init__(
num_qubits, name=f"MBLChainCircuit<{num_qubits}, {depth}>"
)
evolution = MBLChainEvolution(num_qubits, depth, use_cut)
self.compose(evolution, inplace=True)
class MBLChainEvolution(QuantumCircuit):
def __init__(self, num_qubits: int, depth: int, use_cut) -> None:
super().__init__(
num_qubits, name=f"MBLChainEvolution<{num_qubits}, {depth}>"
)
theta = Parameter("θ")
phis = ParameterVector("φ", num_qubits)
for layer in range(depth):
layer_parity = layer % 2
# print("layer parity", layer_parity)
for qubit in range(layer_parity, num_qubits - 1, 2):
# print(qubit)
self.cz(qubit, qubit + 1)
self.u(theta, 0, np.pi, qubit)
self.u(theta, 0, np.pi, qubit + 1)
if (
use_cut
and layer_parity == 0
and (
qubit == num_qubits // 2 - 1
or qubit == num_qubits // 2
)
):
self.append(CutWire(), [num_qubits // 2])
if use_cut and layer < depth - 1 and layer_parity == 1:
if qubit == num_qubits // 2:
self.append(CutWire(), [qubit])
for qubit in range(num_qubits):
self.p(phis[qubit], qubit)num_qubits = 10
depth = 2
mbl = MBLChainCircuit(num_qubits, depth)
mbl.draw("mpl", fold=-1)Output:
について、すべての量子ビットにわたる平均期待値 を計算する。 の理想的な期待値は であり、 であるため、 の理想的な期待値も となる。パラメータ はランダムに選択される。
np.random.seed(42)
phis = list(np.random.rand(mbl.num_parameters - 1))
theta = [0]
params = theta + phis回路を分割するには、分割したい箇所に CutWire を挿入して注釈を付ける必要があります。 このチュートリアルでは、等分分割を採用します。 MBL回路は、関数内の設定 use_cut=True により、 個の量子ビットの後に注釈が適切に挿入されるように設計されています。ここで、 は元の回路に含まれる量子ビットの数です。 また、ランダムに生成されたパラメータを回路に割り当てました。
mbl_cut = MBLChainCircuit(num_qubits, depth, use_cut=True)
mbl_cut.assign_parameters(params, inplace=True)
mbl_cut.draw("mpl", fold=-1)Output:
ステップ2:量子ハードウェア実行に向けた問題の最適化
回路を小さなサブ回路に分割する
qiskit-addon-cuttingここで、を使用して回路を2つの小さな部分回路に分割します。 qiskit-addon-cutting クビットの数を適切に調整することで、ワイヤの切断位置を分割する仮想 Move ゲートを追加する。 それでは、この仮想ゲートを使って回路を作成しましょう。 1本の配線が切断されているため、関連する量子ビットの数は1つ増えます。
mbl_move = cut_wires(mbl_cut)
mbl_move.draw("mpl", fold=-1)Output:
オブザーバブルを構築および拡張する
前述の定義に従えば、観測量は各量子ビットにおける の平均値となる。 しかし、仮想 Move ゲートを挿入すると、回路における有効な量子ビット数は増加する。 また、量子ビット数のこの変化を考慮に入れるため、観測量をそれに応じて展開する必要があります。 仮想 Move ゲートのために追加された余分な量子ビットに対して、観測演算子は常に自明な作用( のように)しか及ぼさないことに注意してください。
observable = PauliList(
["I" * i + "Z" + "I" * (num_qubits - i - 1) for i in range(num_qubits)]
)
observableOutput:
PauliList(['ZIIIIIIIII', 'IZIIIIIIII', 'IIZIIIIIII', 'IIIZIIIIII',
'IIIIZIIIII', 'IIIIIZIIII', 'IIIIIIZIII', 'IIIIIIIZII',
'IIIIIIIIZI', 'IIIIIIIIIZ'])
new_obs = expand_observables(observable, mbl, mbl_move)
new_obsOutput:
PauliList(['ZIIIIIIIIII', 'IZIIIIIIIII', 'IIZIIIIIIII', 'IIIZIIIIIII',
'IIIIZIIIIII', 'IIIIIIZIIII', 'IIIIIIIZIII', 'IIIIIIIIZII',
'IIIIIIIIIZI', 'IIIIIIIIIIZ'])
これで、回路をゲート Move に沿って分割することができ、各サブ回路に対応する元の観測量の部分であるサブ観測量とともに、サブ回路が得られる。
partitioned_problem = partition_problem(circuit=mbl_move, observables=new_obs)
subcircuits = partitioned_problem.subcircuits
subobservables = partitioned_problem.subobservablesここでは、2つのサブ回路を図示します:
subcircuits[0].draw("mpl", fold=-1)Output:
subcircuits[1].draw("mpl", fold=-1)Output:
演算 Move を用いて観測可能領域を拡張するには、データ構造が必要 PauliList となる。 元の回路の期待値を再構築するには、観測量が の SparsePauliOp 形式である必要があります。
M_z = SparsePauliOp(
["I" * i + "Z" + "I" * (num_qubits - i - 1) for i in range(num_qubits)],
coeffs=[1 / num_qubits] * num_qubits,
)前述の通り、各カットにおいて、上流回路はパウリ基底で測定されなければならず、下流回路はその基底の固有状態へと準備されなければならない。 この関 generate_cutting_experiments 数は、再構成に必要なすべての回路と、各回路に関連付けられた係数を生成します。 詳細については、本論文をご覧ください。
subexperiments, coefficients = generate_cutting_experiments(
circuits=subcircuits,
observables=subobservables,
num_samples=np.inf,
)回路をバックエンドにトランスパイルする
シミュレーションのみを行う最初の例として、回路をバックエンドの基本ゲートセットに変換します:
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=133
)
print(backend)Output:
<IBMBackend('ibm_fez')>
ステップ3: Qiskit primitivesを使用して実行する
それでは、各サブ実験を実行してください:
pm_basis = generate_preset_pass_manager(
optimization_level=2, basis_gates=backend.configuration().basis_gates
)
basis_subexperiments = {
label: pm_basis.run(partition_subexpts)
for label, partition_subexpts in subexperiments.items()
}sampler = SamplerV2(mode=AerSimulator())
jobs = {
label: sampler.run(subsystem_subexpts, shots=2**12)
for label, subsystem_subexpts in basis_subexperiments.items()
}ステップ4:後処理を行い、結果を希望の古典形式で返す
ここで、各サブ実験の実行結果を取得し、未カット回路の期待値を再構築します:
# Retrieve results
results = {label: job.result() for label, job in jobs.items()}reconstructed_expval_terms = reconstruct_expectation_values(
results,
coefficients,
subobservables,
)
reconstructed_expval = np.dot(reconstructed_expval_terms, M_z.coeffs).real
reconstructed_expvalOutput:
np.float64(0.9953821063041687)
methods = [
"Uncut",
"Wire cut",
]
values = [
1,
reconstructed_expval,
] # since the ideal expectation value in noiseless simulation is +1
ax = plt.gca()
plt.bar(methods, values, color="#a56eff", width=0.4, edgecolor="#8a3ffc")
ax.set_ylabel(r"$M_Z$", fontsize=12)Output:
Text(0, 0.5, '$M_Z$')
大規模なハードウェアの例
ここでは、60キュービットのMBL回路におけるワイヤーカッティングを実演します。 未カットの回路およびカットされた回路は、 IBM Quantum® ハードウェア上で実行されます:
num_qubits = 60
depth = 2
# construct the circuit
mbl = MBLChainCircuit(num_qubits, depth)
# create parameters
phis = list(np.random.rand(mbl.num_parameters - 1))
theta = [0]
params = theta + phis
# construct the cut circuit
mbl_cut = MBLChainCircuit(num_qubits, depth, use_cut=True)
mbl_cut.assign_parameters(params, inplace=True)
mbl_move = cut_wires(mbl_cut)
# Define observable and expand to account for the wire cut
observable = PauliList(
["I" * i + "Z" + "I" * (num_qubits - i - 1) for i in range(num_qubits)]
)
new_obs = expand_observables(observable, mbl, mbl_move)
# Construct a SparsePauliOp version of the observable for later use in reconstruction
M_z = SparsePauliOp(
["I" * i + "Z" + "I" * (num_qubits - i - 1) for i in range(num_qubits)],
coeffs=[1 / num_qubits] * num_qubits,
)
# Partition the circuit and get subcircuits and subobservables
partitioned_problem = partition_problem(circuit=mbl_move, observables=new_obs)
subcircuits = partitioned_problem.subcircuits
subobservables = partitioned_problem.subobservables
# Obtain subexperiments and coefficients
subexperiments, coefficients = generate_cutting_experiments(
circuits=subcircuits,
observables=subobservables,
num_samples=np.inf,
)
# Transpile the subexperiments to the backend
pm = generate_preset_pass_manager(optimization_level=2, backend=backend)
isa_subexperiments = {
label: pm.run(partition_subexpts)
for label, partition_subexpts in subexperiments.items()
}
# Execute the subexperiments and retrieve results
with Batch(backend=backend) as batch:
sampler = SamplerV2(mode=batch)
sampler.options.environment.job_tags = ["TUT_WC"]
jobs = {
label: sampler.run(subsystem_subexpts, shots=2**12)
for label, subsystem_subexpts in isa_subexperiments.items()
}
results = {label: job.result() for label, job in jobs.items()}
# Reconstruct the expectation value of the original observable
reconstructed_expval_terms = reconstruct_expectation_values(
results,
coefficients,
subobservables,
)
reconstructed_expval = np.dot(reconstructed_expval_terms, M_z.coeffs).real
# Compute the uncut circuit to obtain the noisy expectation value for comparison
sampler = SamplerV2(mode=backend)
sampler.options.environment.job_tags = ["TUT_WC"]
if mbl.num_clbits == 0:
mbl.measure_all()
isa_mbl = pm.run(mbl)
pub = (isa_mbl, params)
uncut_job = sampler.run([pub])
uncut_counts = uncut_job.result()[0].data.meas.get_counts()
uncut_expval = sampled_expectation_value(uncut_counts, M_z)
# visualize the results
ax = plt.gca()
methods = ["uncut", "cut"]
values = [uncut_expval, reconstructed_expval]
plt.bar(methods, values, color="#a56eff", width=0.4, edgecolor="#8a3ffc")
plt.axhline(y=1, color="k", linestyle="--")
plt.text(0.3, 0.95, "Exact result")
plt.show()Output:
uncut_expvalOutput:
0.9202473958333336
次のステップ
この作品に興味を持たれた方は、以下の資料もご参照ください:
参照
[1] Peng, T、 ハロー、A. Ozols, M., & Wu, X. (2020). 小型量子コンピュータで大規模量子回路をシミュレートする。 Physical review letters, 125(15), 150504.
[2] Tang, W、 トメッシュ、T、 スチャラ、M、 Larson, J., & Martonosi, M. (2021, April). Cutqc:大規模量子回路評価のための小型量子コンピュータの使用。 プログラミング言語とオペレーティングシステムのアーキテクチャサポートに関する第26回ACM国際会議予稿集 (pp. 473-486).
[3] パーリン、M. A、 サリーム、Z. H、 スチャラ、M.、オズボーン、J. C. (2021). 最尤トモグラフィによる量子回路切断。 量子情報, 7(1), 64.
[4] Majumdar, R., & Wood, C. J. (2022). エラーを軽減した量子回路切断 arXiv preprint arXiv:2211.13431.
[5] カレ、T、 マジュムダル、R、 サングル、R、 レイ、A、 Seshadri, P. V., & Simmhan, Y. (2023). 量子古典ワークロードの並列化:分割技術の影響のプロファイリング。 2023 IEEE International Conference on Quantum Computing and Engineering (QCE) (Vol. 1, pp. 990-1000). IEEE。
[6] Bhoumik, D、 マジュムダル、R、 Saha, A., & Sur-Kolay, S. (2023). ノイズと時間の最適化を伴う量子回路の分散スケジューリング. arXiv preprint arXiv:2309.06005.
[7] Majumdar, R. (2024). 「離散量子計算回路におけるリソースとノイズの効率的な低減」(博士論文、インド統計研究所-コルカタ)。 https://www.proquest.com/openview/b481def90b1cc80e6b58a77c99e8385c/1?pq-origsite=gscholar&cbl=2026366&diss=y