深さ削減のための回路切断
使用時間の目安イーグルプロセッサーで8分(注:あくまでも目安です。 ランタイムは異なるかもしれない)。
背景
このチュートリアルでは、量子回路のゲートを切断して回路の深さを減らすための Qiskit pattern 。 回路切断に関するより詳細な議論については、 回路切断Qiskitアドオンのドキュメントをご覧ください。
要件
このチュートリアルを始める前に、以下のものがインストールされていることを確認してください:
- Qiskit SDK v2.0 またはそれ以降、 可視化サポート付き
- Qiskit Runtime v0.22 またはそれ以降 (
pip install qiskit-ibm-runtime) - 回路切断 Qiskit アドオン v0.9.0 以降 (
pip install qiskit-addon-cutting)
セットアップ
import numpy as np
from qiskit.circuit.library import EfficientSU2
from qiskit.quantum_info import PauliList, Statevector, SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_addon_cutting import (
cut_gates,
generate_cutting_experiments,
reconstruct_expectation_values,
)
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2ステップ1:古典的な入力を量子問題にマッピングする
ドキュメントに概説されている4つのステップを使用して、Qiskitパターンを実装します。 この場合、スワップ・ゲートになるゲートをカットし、より浅い回路でサブ実験を実行することで、ある深さの回路で期待値をシミュレートする。 ゲート切断は、ステップ2(離れたゲートを分解して量子実行のために回路を最適化する)とステップ4(元の回路の期待値を再構成する後処理)に関連している。 最初のステップでは、Qiskit回路ライブラリから回路を生成し、いくつかのobservablesを定義する。
- インプット回路を定義するための古典的なパラメータ
- 出力抽象回路と観測値
circuit = EfficientSU2(num_qubits=4, entanglement="circular").decompose()
circuit.assign_parameters([0.4] * len(circuit.parameters), inplace=True)
observables = PauliList(["ZZII", "IZZI", "IIZZ", "XIXI", "ZIZZ", "IXIX"])
circuit.draw("mpl", scale=0.8, style="iqp")Output:
ステップ2:量子ハードウェア実行に向けた問題の最適化
- 入力抽象回路と観測値
- 出力:出力:トランスパイルド回路の深さを減らすために遠くのゲートをカットすることによって生成されるターゲット回路と観測値
初期レイアウトは、量子ビット3と0の間のゲートを実行するために2回のスワップを必要とし、量子ビットを初期位置に戻すためにさらに2回のスワップを必要とするものを選んだ。 私たちは、プリセット・パス・マネージャーで利用可能な最高レベルの最適化である optimization_level=3。
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, min_num_qubits=circuit.num_qubits, simulator=False
)
pm = generate_preset_pass_manager(
optimization_level=3, initial_layout=[0, 1, 2, 3], backend=backend
)
transpiled_qc = pm.run(circuit)
print(f"Transpiled circuit depth: {transpiled_qc.depth()}")
transpiled_qc.draw("mpl", scale=0.4, idle_wires=False, style="iqp", fold=-1)Output:
Transpiled circuit depth: 103
遠隔ゲートを検索して切り取る: 非局所量子ビット(0と3)を接続する遠隔ゲートを、そのインデックスを指定 TwoQubitQPDGate することでオブジェクトに置き換える。 cut_gates 指定されたインデ TwoQubitQPDGate ックスのゲートをオブジェクトで置き換え、さらに各ゲート分解に対応 QPDBasis するインスタンスのリストを返します。 オブジェクト QPDBasis は、カットゲートを単一量子ビット操作に分解する方法に関する情報を含みます。
# Find the indices of the distant gates
cut_indices = [
i
for i, instruction in enumerate(circuit.data)
if {circuit.find_bit(q)[0] for q in instruction.qubits} == {0, 3}
]
# Decompose distant CNOTs into TwoQubitQPDGate instances
qpd_circuit, bases = cut_gates(circuit, cut_indices)
qpd_circuit.draw("mpl", scale=0.8)Output:
バックエンドで実行するサブ実験を生成する。 generate_cutting_experiments は、 TwoQubitQPDGate インスタンスと観測値を含む回路を PauliList として受け取る。
フルサイズの回路の期待値をシミュレートするために、分解されたゲートの結合準確率分布から多数のサブ実験が生成され、1つ以上のバックエンドで実行される。 分布から取られたサンプルの数は num_samples で制御され、各ユニークなサンプルに対して1つの結合係数が与えられます。 係数の計算方法の詳細については、 説明資料を参照のこと。
# Generate the subexperiments and sampling coefficients
subexperiments, coefficients = generate_cutting_experiments(
circuits=qpd_circuit, observables=observables, num_samples=np.inf
)比較のため、遠くのゲートをカットするとQPDサブ実験が浅くなることがわかる :以下は、QPD回路から生成された、任意に選択されたサブ実験の例である。 その深さは半分以下になった。 より深い回路の期待値を再構築するためには、このような確率的なサブ実験の多くを生成し、評価しなければならない。
# Transpile the decomposed circuit to the same layout
transpiled_qpd_circuit = pm.run(subexperiments[100])
print(f"Original circuit depth after transpile: {transpiled_qc.depth()}")
print(
f"QPD subexperiment depth after transpile: {transpiled_qpd_circuit.depth()}"
)
transpiled_qpd_circuit.draw(
"mpl", scale=0.6, style="iqp", idle_wires=False, fold=-1
)Output:
Original circuit depth after transpile: 103
QPD subexperiment depth after transpile: 46
一方、カットすることで余計なサンプリングが必要になる。 ここでは、3つのCNOTゲートをカットした結果、サンプリング・オーバーヘッドが 。回路カットで発生するサンプリング・オーバーヘッドについては、 Circuit Knitting Toolboxのドキュメントを参照。
print(f"Sampling overhead: {np.prod([basis.overhead for basis in bases])}")Output:
Sampling overhead: 729.0
ステップ3: Qiskit primitivesを使用して実行する
Samplerプリミティブを使用して、対象の回路(「サブ実験」)を実行します。
- 入力ターゲット回路
- アウトプット準確率分布
# Transpile the subexperiments to the backend's instruction set architecture (ISA)
isa_subexperiments = pm.run(subexperiments)
# Set up the IBM Quantum Sampler primitive. For a fake backend, this will use a local simulator.
sampler = SamplerV2(backend)
# Submit the subexperiments
job = sampler.run(isa_subexperiments)# Retrieve the results
results = job.result()print(job.job_id())Output:
czypg1r6rr3g008mgp6g
ステップ4:後処理を行い、結果を希望の古典形式で返す
サブ実験結果、サブ観測値、サンプリング係数を用いて、元の回路の期待値を再構築する。
入力準確率分布 出力再構成された期待値
reconstructed_expvals = reconstruct_expectation_values(
results,
coefficients,
observables,
)
# Reconstruct final expectation value
final_expval = np.dot(reconstructed_expvals, [1] * len(observables))
print("Final reconstructed expectation value")
print(final_expval)Output:
Final reconstructed expectation value
1.0751342773437473
ideal_expvals = [
Statevector(circuit).expectation_value(SparsePauliOp(observable))
for observable in observables
]
print("Ideal expectation value")
print(np.dot(ideal_expvals, [1] * len(observables)).real)Output:
Ideal expectation value
1.2283177520039992
チュートリアル調査
このチュートリアルに関するご意見・ご感想をお寄せください。 あなたの洞察は、私たちのコンテンツの提供とユーザーエクスペリエンスを向上させるのに役立ちます。