メインコンテンツへスキップ

古典フィードフォワードと制御フロー

パッケージバージョン

このページのコードは以下の要件を使用して開発されました。 これらのバージョン以降を使用することを推奨します。

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
動的回路がすべてのバックエンドで利用可能になりました

動的回路の新バージョンが、すべてのユーザーのすべてのバックエンドで利用可能になりました。ユーティリティスケールで動的回路を実行できるようになりました。詳細はお知らせをご覧ください。

動的回路は強力なツールであり、量子回路の実行中にQubitを測定し、その中回路測定の結果に基づいて回路内で古典的な論理演算を実行することができます。このプロセスは_古典フィードフォワード_とも呼ばれます。動的回路を最大限に活用する方法についてはまだ初期段階ですが、量子研究コミュニティはすでにいくつかのユースケースを特定しています。例えば以下のものがあります:

しかし、動的回路がもたらすこれらの改善にはトレードオフが伴います。中回路測定と古典演算は、通常、2-Qubitゲートよりも実行時間が長く、この時間の増加が回路深さの削減による利点を相殺する可能性があります。そのため、IBM Quantum®が動的回路の新バージョンをリリースする中、中回路測定の時間短縮は改善の重点分野となっています。

OpenQASM 3仕様では多くの制御フロー構造が定義されていますが、Qiskit Runtimeは現在、条件付きif文のみをサポートしています。Qiskit SDKでは、これはQuantumCircuitif_testメソッドに対応します。このメソッドはコンテキストマネージャーを返し、通常はwith文で使用されます。このガイドでは、この条件文の使用方法について説明します。

備考

このガイドのコード例では、中回路測定に標準のmeasure命令を使用しています。ただし、バックエンドがサポートしている場合は、代わりにMidCircuitMeasure命令を使用することを推奨します。詳細は中回路測定のドキュメントをご覧ください。

if

if文は、古典ビットまたはレジスタの値に基づいて条件付きで演算を実行するために使用します。

以下の例では、QubitにHadamardゲートを適用して測定します。結果が1の場合、QubitにXゲートを適用します。これにより、Qubitを0の状態に戻します。次に、Qubitを再度測定します。結果として得られる測定結果は、100%の確率で0になるはずです。

# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister

qubits = QuantumRegister(1)
clbits = ClassicalRegister(1)
circuit = QuantumCircuit(qubits, clbits)
(q0,) = qubits
(c0,) = clbits

circuit.h(q0)
# Use MidCircuitMeasure() if it's supported by the backend.
# circuit.append(MidCircuitMeasure(), [q0], [c0])
circuit.measure(q0, c0)
with circuit.if_test((c0, 1)):
circuit.x(q0)
circuit.measure(q0, c0)
circuit.draw("mpl")

# example output counts: {'0': 1024}

Output of the previous code cell

with文には代入ターゲットを指定することができます。これ自体もコンテキストマネージャーであり、格納してその後elseブロックの作成に使用できます。elseブロックは、ifブロックの内容が実行されない場合に常に実行されます。

以下の例では、2つのQubitと2つの古典ビットのレジスタを初期化します。最初のQubitにHadamardゲートを適用して測定します。結果が1の場合、2番目のQubitにHadamardゲートを適用します。それ以外の場合は、2番目のQubitにXゲートを適用します。最後に、2番目のQubitも測定します。

qubits = QuantumRegister(2)
clbits = ClassicalRegister(2)
circuit = QuantumCircuit(qubits, clbits)
(q0, q1) = qubits
(c0, c1) = clbits

circuit.h(q0)
circuit.measure(q0, c0)
with circuit.if_test((c0, 1)) as else_:
circuit.h(q1)
with else_:
circuit.x(q1)
circuit.measure(q1, c1)

circuit.draw("mpl")

# example output counts: {'01': 260, '11': 272, '10': 492}

Output of the previous code cell

単一の古典ビットを条件とするだけでなく、複数のビットで構成される古典レジスタの値を条件とすることも可能です。

以下の例では、2つのQubitにHadamardゲートを適用して測定します。結果が01(つまり、最初のQubitが1で2番目のQubitが0)の場合、3番目のQubitにXゲートを適用します。最後に、3番目のQubitを測定します。なお、わかりやすくするために、if条件で3番目の古典ビットの状態(0)を明示的に指定しています。回路図では、条件は条件付けされている古典ビット上の丸で示されます。黒い丸は1への条件付けを示し、白い丸は0への条件付けを示します。

qubits = QuantumRegister(3)
clbits = ClassicalRegister(3)
circuit = QuantumCircuit(qubits, clbits)
(q0, q1, q2) = qubits
(c0, c1, c2) = clbits

circuit.h([q0, q1])
circuit.measure(q0, c0)
circuit.measure(q1, c1)
with circuit.if_test((clbits, 0b001)):
circuit.x(q2)
circuit.measure(q2, c2)

circuit.draw("mpl")

# example output counts: {'101': 269, '011': 260, '000': 252, '010': 243}

Output of the previous code cell

古典式

Qiskitの古典式モジュールqiskit.circuit.classicalには、回路実行中に古典値に対するランタイム演算の試験的な表現が含まれています。ハードウェアの制限により、現在はQuantumCircuit.if_test()の条件のみがサポートされています。

以下の例では、パリティの計算を使用して動的回路でn-QubitのGHZ状態を作成する方法を示します。まず、隣接するQubitにn/2n/2個のBellペアを生成します。次に、ペア間のCNOTゲート層を使用してこれらのペアを結合します。その後、以前のすべてのCNOTゲートのターゲットQubitを測定し、測定された各Qubitを状態0\vert 0 \rangleにリセットします。先行するすべてのビットのパリティが奇数である測定されていないすべてのサイトにXXを適用します。最後に、測定されたQubitにCNOTゲートを適用して、測定で失われたエンタングルメントを再確立します。

パリティ計算では、構築された式の最初の要素はPythonオブジェクトmr[0]Valueノードに持ち上げることを含みます(liftは任意のオブジェクトを古典式に変換するために使用されます)。mr[1]と後続の可能な古典レジスタについては、これは必要ありません。これらはexpr.bit_xorへの入力であり、必要な持ち上げはこれらの場合に自動的に行われるためです。このような式はループやその他の構造で構築できます。

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.classical import expr

num_qubits = 8
if num_qubits % 2 or num_qubits < 4:
raise ValueError("num_qubits must be an even integer ≥ 4")
meas_qubits = list(range(2, num_qubits, 2)) # qubits to measure and reset

qr = QuantumRegister(num_qubits, "qr")
mr = ClassicalRegister(len(meas_qubits), "m")
qc = QuantumCircuit(qr, mr)

# Create local Bell pairs
qc.reset(qr)
qc.h(qr[::2])
for ctrl in range(0, num_qubits, 2):
qc.cx(qr[ctrl], qr[ctrl + 1])

# Glue neighboring pairs
for ctrl in range(1, num_qubits - 1, 2):
qc.cx(qr[ctrl], qr[ctrl + 1])

# Measure boundary qubits between pairs,reset to 0
for k, q in enumerate(meas_qubits):
qc.measure(qr[q], mr[k])
qc.reset(qr[q])

# Parity-conditioned X corrections
# Each non-measured qubit gets flipped iff the parity (XOR) of all
# preceding measurement bits is 1
for tgt in range(num_qubits):
if tgt in meas_qubits: # skip measured qubits
continue
# all measurement registers whose physical qubit index < tgt
left_bits = [k for k, q in enumerate(meas_qubits) if q < tgt]
if not left_bits: # skip if list empty
continue

# build XOR-parity expression
parity = expr.lift(
mr[left_bits[0]]
) # lift the first bit to Value so it will be treated like a boolean.
for k in left_bits[1:]:
parity = expr.bit_xor(
mr[k], parity
) # calculate parity with all other bits
with qc.if_test(parity): # Add X if parity is 1
qc.x(qr[tgt])

# Re-entangle measured qubits
for ctrl in range(1, num_qubits - 1, 2):
qc.cx(qr[ctrl], qr[ctrl + 1])
qc.draw(output="mpl", style="iqp", idle_wires=False, fold=-1)

Output of the previous code cell

動的回路をサポートするバックエンドを見つける

アカウントがアクセスでき、動的回路をサポートするすべてのバックエンドを見つけるには、以下のようなコードを実行します。この例では、ログイン資格情報を保存済みであることを前提としています。Qiskit Runtimeサービスアカウントを初期化する際に明示的に資格情報を指定することもできます。これにより、特定のインスタンスやプランタイプで利用可能なバックエンドを表示できます。

注意事項
  • アカウントで利用可能なバックエンドは、資格情報で指定されたインスタンスによって異なります。
  • 動的回路の新バージョンが、すべてのユーザーのすべてのバックエンドで利用可能になりました。詳細はお知らせをご覧ください。
from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
dc_backends = service.backends(dynamic_circuits=True)
print(dc_backends)
[<IBMBackend('ibm_pittsburgh')>, <IBMBackend('ibm_boston')>, <IBMBackend('ibm_fez')>, <IBMBackend('ibm_miami')>, <IBMBackend('ibm_marrakesh')>, <IBMBackend('ibm_torino')>, <IBMBackend('ibm_kingston')>]

Qiskit Runtimeの制限事項

Qiskit Runtimeで動的回路を実行する際は、以下の制約事項に注意してください。

  • 制御エレクトロニクスの物理メモリが限られているため、if文の数とそのオペランドのサイズにも制限があります。この制限は、ジョブ(回路ではなく)のブロードキャスト数とブロードキャストされるビット数の関数です。

    if条件を処理する際、その評価を行うために測定データを制御ロジックに転送する必要があります。ブロードキャストはユニークな古典データの転送であり、ブロードキャストされるビットは転送される古典ビットの数です。以下を考慮してください:

    c0 = ClassicalRegister(3)
    c1 = ClassicalRegister(5)
    ...
    with circuit.if_test((c0, 1)) ...
    with circuit.if_test((c0, 3)) ...
    with circuit.if_test((c1[2], 1)) ...

    上記のコード例では、c0に対する最初の2つのif_testオブジェクトは、c0の内容が変更されていないため再ブロードキャストの必要がなく、1つのブロードキャストと見なされます。c1に対するif_testは2番目のブロードキャストです。最初のブロードキャストはc0の3ビットすべてをブロードキャストし、2番目は1ビットのみをブロードキャストするため、合計4ブロードキャストビットになります。

    現在、毎回60ビットをブロードキャストする場合、ジョブは約300回のブロードキャストを持つことができます。しかし、毎回1ビットのみをブロードキャストする場合、ジョブは2400回のブロードキャストを持つことができます。

  • if_test文で使用されるオペランドは32ビット以下でなければなりません。したがって、ClassicalRegister全体を比較する場合、そのClassicalRegisterのサイズは32ビット以下でなければなりません。ただし、ClassicalRegisterから1ビットのみを比較する場合、オペランドは1ビットのみであるため、そのClassicalRegisterのサイズは任意です。

    例えば、「無効」のコードブロックはcrが32ビットを超えているため機能しません。しかし、「有効」のコードブロックに示すように、1ビットのみをテストする場合は32ビットより広い古典レジスタを使用できます。

       cr = ClassicalRegister(50)
    qr = QuantumRegister(50)
    circuit = QuantumCircuit(qr, cr)
    ...
    circ.measure(qr, cr)
    with circ.if_test((cr, 15)):
    ...
  • ネストされた条件文はサポートされていません。例えば、以下のコードブロックはif_testの中に別のif_testがあるため機能しません:

       c1 = ClassicalRegister(1, "c1")
    c2 = ClassicalRegister(2, "c2")
    ...
    with circ.if_test((c1, 1)):
    with circ.if_test(c2, 1)):
    ...
  • 条件文内でのresetや測定はサポートされていません。

  • 算術演算はサポートされていません。

  • QiskitおよびQiskit RuntimeでサポートされているOpenQASM 3の機能については、OpenQASM 3機能テーブルを参照してください。

  • OpenQASM 3(QuantumCircuitの代わりに)をQiskit Runtimeプリミティブへの回路入力フォーマットとして使用する場合、Qiskitに読み込める命令のみがサポートされています。例えば、古典的な演算はQiskitに読み込めないためサポートされていません。詳細はOpenQASM 3プログラムをQiskitにインポートするを参照してください。

  • forwhileswitch命令はサポートされていません。

EstimatorとともにDynamic Circuitsを使用する

EstimatorはDynamic Circuitsをサポートしていないため、Samplerを使用して独自の測定回路を構築できます。または、Dynamic CircuitsをサポートするExecutorプリミティブを使用することもできます。

Estimatorの動作を再現するには、以下のプロセスに従ってください:

  1. すべてのオブザーバブルの項をパーティションにグループ化します。例えば、PauliList APIを使用してこれを行うことができます。
    備考

    BitArrayプリミティブ属性を使用して、提供されたオブザーバブルの期待値を計算できます。

  2. 各パーティションに対して1つの基底変換回路を実行します(各パーティションに必要な基底変換を実行します)。詳細は、Measurement basesアドオンユーティリティのmeasurement_basesモジュールを参照してください。ユーティリティを始めましょう。
  3. 各パーティションの結果を合計します。

次のステップ

推奨事項
  • stretchを使用した正確なDynamic Decouplingの実装方法を学びましょう。
  • 回路時間を短縮する中回路測定について学びましょう。
  • 回路スケジュール可視化を使用して動的回路をデバッグおよび最適化しましょう。