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

古典フィードフォワードと制御フロー(動的回路)

パッケージバージョン

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

qiskit[all]~=2.4.0

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

Qiskitは古典フィードフォワードのための4つの制御フロー構造をサポートしており、それぞれQuantumCircuitのメソッドとして実装されています。構造とその対応するメソッドは次のとおりです:

これらのメソッドはそれぞれコンテキストマネージャーを返し、通常はwith文と組み合わせて使用されます。本ガイドの残りの部分では、これらの構造とその使用方法について説明します。

注意

量子ハードウェア上での古典フィードフォワードおよび制御フロー演算には、プログラムに影響を与える可能性のある制限があります。詳細については、動的回路を実行するを参照してください。

if

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

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

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

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

circuit.h(q0)
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

Switch文

Switch文は、古典ビットまたはレジスタの値に基づいてアクションを選択するために使用します。if文に似ていますが、分岐ロジックにより多くのケースを指定することができます。以下の例では、QubitにHadamardゲートを適用して測定します。結果が0の場合はQubitにXゲートを適用し、結果が1の場合はZゲートを適用します。結果として得られる測定結果は、100%の確率で1になるはずです。

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

circuit.h(q0)
circuit.measure(q0, c0)
with circuit.switch(c0) as case:
with case(0):
circuit.x(q0)
with case(1):
circuit.z(q0)
circuit.measure(q0, c0)

circuit.draw("mpl")

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

Output of the previous code cell

上記の例では単一の古典ビットを使用したため、可能なケースは2つだけでした。そのため、if-else文を使って同じ結果を得ることもできます。Switch caseは、複数のビットで構成される古典レジスタの値に基づいて分岐する場合に主に役立ちます。次の例では、デフォルトケースの作成方法を示します。デフォルトケースは、前のケースのいずれにも一致しない場合に実行されます。なお、Switch文では、ブロックのうち1つしか実行されません。フォールスルーはありません。

以下の例では、2つのQubitにHadamardゲートを適用して測定します。結果が00または11の場合、3番目のQubitにZゲートを適用します。結果が01の場合はYゲートを適用します。前のケースのいずれにも一致しない場合はXゲートを適用します。最後に、3番目のQubitを測定します。

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.switch(clbits) as case:
with case(0b000, 0b011):
circuit.z(q2)
with case(0b001):
circuit.y(q2)
with case(case.DEFAULT):
circuit.x(q2)
circuit.measure(q2, c2)

circuit.draw("mpl")

# example output counts: {'101': 267, '110': 249, '011': 265, '000': 243}

Output of the previous code cell

Forループ

Forループは、古典値のシーケンスを繰り返し処理し、各繰り返しで何らかの演算を実行するために使用します。

次の例では、Forループを使用してQubitに5回Xゲートを適用し、その後測定します。奇数回のXゲートを実行するため、全体的な効果はQubitを0の状態から1の状態に反転させることです。

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

with circuit.for_loop(range(5)) as _:
circuit.x(q0)
circuit.measure(q0, c0)

circuit.draw("mpl")

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

Output of the previous code cell

Whileループ

Whileループは、ある条件が満たされている間、命令を繰り返すために使用します。

以下の例では、2つのQubitにHadamardゲートを適用して測定します。次に、測定結果が11である間この手順を繰り返すWhileループを作成します。その結果、最終的な測定結果が11になることはなく、残りの可能性がほぼ同じ頻度で現れます。

qubits = QuantumRegister(2)
clbits = ClassicalRegister(2)
circuit = QuantumCircuit(qubits, clbits)

q0, q1 = qubits
c0, c1 = clbits

circuit.h([q0, q1])
circuit.measure(q0, c0)
circuit.measure(q1, c1)
with circuit.while_loop((clbits, 0b11)):
circuit.h([q0, q1])
circuit.measure(q0, c0)
circuit.measure(q1, c1)

circuit.draw("mpl")

# example output counts: {'01': 334, '10': 368, '00': 322}

Output of the previous code cell

古典式

Qiskitの古典式モジュールqiskit.circuit.classicalには、回路実行中に古典値に対するランタイム演算の試験的な表現が含まれています。

以下の例では、パリティの計算を使用して動的回路で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

Store

store 命令を使用すると、古典式が繰り返し使用される場合に、その計算結果を保存することができます。演算は自動的に並列化されるため、実行時の効率が大幅に向上します。

例えば、(¬A[0])(¬A[1])(¬A[2])(\neg A[0]) \oplus (\neg A[1]) \oplus (\neg A[2]) \ldots よりも、B=¬AB = \neg A として B[0]B[1]B[2]B[0] \oplus B[1] \oplus B[2] \ldots と記述する方が自然であり、実行時の効率も高くなります。前者は否定をXORチェーンの前に単一の並列ステップで計算するのに対し、後者は式の中で各否定を逐次的に評価します。

完全な例:

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

qregs = QuantumRegister(4, "q")
creg = ClassicalRegister(3, "c")
# temp is a plain ClassicalRegister used as the store target
temp = ClassicalRegister(3, "temp")
qc = QuantumCircuit(qregs, creg, temp)

qc.h([0, 1, 2])
qc.measure([0, 1, 2], creg)

# Store bit-NOT of the full 3-bit register into temp
qc.store(temp, expr.bit_not(creg))

# Compute parity of temp using bit-indexed XOR
parity = expr.bit_xor(
expr.bit_xor(expr.index(temp, 0), expr.index(temp, 1)),
expr.index(temp, 2),
)

# Flip q3 if parity of ~creg is 1
with qc.if_test(parity):
qc.x(3)

qc.measure([0, 1, 2], creg)

qc.draw("mpl")

Output of the previous code cell

次のステップ

推奨事項