古典的なフィードフォワードと制御フロー(動的回路)
このページのコードは、以下の要件に基づいて開発された。 これらのバージョンまたは新しいバージョンの使用をお勧めします。
qiskit[all]~=2.5.1
動的回路は、量子回路の実行中に量子ビットを測定し、その結果に基づいて回路内で古典論理演算を実行できる強力なツールである。 このプロセスは古典的フィードフォワードとも呼ばれる。 動的回路をどのように活用するのが最善かを理解するのはまだ日が浅いが、量子研究コミュニティはすでに、以下のような多くのユースケースを特定している:
- 効率的な量子状態生成( GHZ状態、 W状態 (W状態の詳細については 「フィードフォワードを用いた浅い回路による状態生成」 も参照)、および広範な行列積状態など)
- 浅い回路を用いた同一チップ上の量子ビット間の効率的な長距離エンタングルメント
- IQPのような回路の効率的なサンプリング
QuantumCircuitQiskit は、従来のフィードフォワード向けに 4 つの制御フロー構文をサポートしており、それぞれが のメソッドとして実装されています。 これらの構造体と、それに対応するメソッドは以下の通りです:
- if文 -
QuantumCircuit.if_test - switch文 -
QuantumCircuit.switch - forループ -
QuantumCircuit.for_loop - whileループ -
QuantumCircuit.while_loop
with これらの各メソッドはコンテキストマネージャー を返し、通常は 文で使用されます。 このガイドの残りの部分では、これらの構文それぞれについて、その詳細と使用方法を解説します。
量子ハードウェア上で、従来のフィードフォワード演算や制御フロー演算にはいくつかの制限があり、これらがプログラムに影響を与える可能性があります。 詳細については、 「動的回路の実行」 を参照してください。
if ステートメント
if ステートメントは、古典的なビットやレジスタの値に基づいて条件付きで演算を実行するために使用される。
以下の例では、量子ビットにハダマードゲートを適用し、それを測定する。 もし結果が1であれば、量子ビットにXゲートを適用し、0状態に反転させる。 その後、再び量子ビットを測定する。 その結果、測定結果は100%の確率で0になるはずである。
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:
with 、その代入先を指定することができる。その代入先は、それ自体がコンテキスト・マネージャーであり、 if 、そのブロックの内容が実行されないときはいつでも実行されるelseブロックを作成するために使用される。
以下の例では、2つの量子ビットと2つの古典ビットでレジスタを初期化している。 最初の量子ビットにハダマードゲートを適用し、それを測定する。 もし結果が1であれば、2番目の量子ビットにハダマードゲートを適用し、そうでなければ2番目の量子ビットにXゲートを適用する。 最後に、2番目の量子ビットも測定する。
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:
単一の古典的ビットに対する条件付けに加えて、複数のビットで構成される古典的レジスタの値に対する条件付けも可能である。
以下の例では、2つの量子ビットにハダマードゲートを適用し、それらを測定します。 結果が 01、すなわち最初の量子ビットが1で2番目の量子ビットが0である場合、3番目の量子ビットにXゲートを適用する。 最後に、3番目の量子ビットを測定します。 明確化のため、条件 if 式において第三の古典ビットの状態(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:
switch ステートメント
switch文は、特定のビットやレジスタの値に基づいて処理を選択するために使用されます。 if文と似ていますが、分岐ロジックに対してより多くのケースを指定することができます。 以下の例では、1つの量子ビットにアダマールゲートを適用し、その状態を測定します。 結果が0の場合は、その量子ビットに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:
上記の例では単一の古典的なビットを使用していたため、考えられるケースは2つしかなく、if-else文を使っても同じ結果を得ることができたでしょう。 switch文は、主に複数のビットで構成される従来のレジスタの値に基づいて分岐を行う場合に役立ちます。 次の例は、それ以前のケースのいずれも実行されなかった場合に実行されるデフォルトのケースを定義する方法を示しています。 switch文では、いずれかのブロックが1つだけ実行されることに注意してください。 フォールスルーは発生しません。
以下の例では、2つの量子ビットにアダマールゲートを適用し、その状態を測定します。 結果が「00」または「11」のいずれかである場合、3番目の量子ビットにZゲートを適用する。 結果が 01 の場合は、Y ゲートを適用する。 前述のいずれのケースにも該当しない場合は、Xゲートを適用する。 最後に、3番目の量子ビットを測定します。
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:
for ループ
forループは、一連の値に対して反復処理を行い、各反復ごとに何らかの操作を実行するために使用されます。
次の例では、forループを使用して1つの量子ビットに5つのXゲートを適用し、その後、その量子ビットを測定しています。 奇数のXゲートが実行されるため、全体としてクビットが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:
while ループ
whileループは、ある条件が満たされている間、命令を繰り返すために使用されます。
以下の例では、2つの量子ビットにアダマールゲートを適用し、その状態を測定します。 そして、測定結果が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:
古典的表現
Qiskitの古典式モジュール qiskit.circuit.classical には、回路の実行中に古典値に対して行われる実行時操作の探索的な表現が含まれています。
次の例では、パリティの計算を利用して、動的回路を使ってn量子ビットのGHZ状態を作ることができることを示している。 まず、隣接する量子ビットに ベル・ペアを生成する。 次に、ペアとペアの間にCNOTゲートのレイヤーを使って、これらのペアを接着する。 次に、すべての先行するCNOTゲートのターゲット量子ビットを測定し、各測定量子ビットを状態 にリセットする。先行するすべてのビットのパリティが奇数である未測定のすべてのサイトに 。 最後に、CNOTゲートが測定された量子ビットに適用され、測定で失われたエンタングルメントが再び確立される。
パリティ計算において、構築された式の最初の要素は、 Pythonmr[0] オブジェクトをノードに昇格させることを含む( liftValue は任意のオブジェクトを古典式に変換するために使用される)。 これは、および 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:
Store
その式を繰り返し使用する場合は、この store 命令を使って通常の式の結果を保存することができます。 処理は自動的に並列化されるため、実行時のコードの効率が大幅に向上します。
例えば、実行時には、 と書くよりも、 と書く方がより自然で効率的です(ここで、 )。前者の場合、式の中で各否定演算を順次評価するのではなく、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:
次のステップ
- ストレッチを使用して正確な動的デカップリングを実装する方法を学びましょう。
- 回路スケジュールの可視化機能を活用して、動的回路のデバッグと最適化を行ってください。
- 動的回路を実行する。