量子回路の構造は古典制御フローに依存することが多くあります。量子ビットのイテレーション、グラフのエッジに基づくゲート適用、ゲート列の条件分岐などです。Qamomileではqmc.range、qmc.items、if分岐、whileループでこれらをサポートしています。
この章では以下を扱います:
qmc.range()によるループqmc.items()による辞書のイテレーション測定結果に対する
if/whileによる回路途中の分岐
# 最新のQamomileをpipからインストールします!
# !pip install qamomileimport qamomile.circuit as qmc
from qamomile.qiskit import QiskitTranspiler
transpiler = QiskitTranspiler()qmc.rangeループ¶
qmc.rangeはstart、stop、stepを引数に取ることができます。ここでは偶数番目の量子ビットにHゲートを適用し、隣接ペアをCXでエンタングルする量子カーネルを作ってみます。
@qmc.qkernel
def hadamard_chain(n: qmc.UInt) -> qmc.Vector[qmc.Bit]:
q = qmc.qubit_array(n, name="q")
# 偶数番目の量子ビットに H を適用
for i in qmc.range(0, n, 2):
q[i] = qmc.h(q[i])
# 隣接するペアをエンタングル
for i in qmc.range(n - 1):
q[i], q[i + 1] = qmc.cx(q[i], q[i + 1])
return qmc.measure(q)hadamard_chain.draw(n=5, fold_loops=False)
qmc.itemsによるスパースな相互作用データの処理¶
QAOAやVQEなど多くの量子アルゴリズムでは、グラフや相互作用マップで決まる特定の量子ビットペアにのみゲートを適用します。全ペアをループするのではなく、相互作用の辞書を渡してqmc.items()でイテレーションできます。
辞書型にはQamomileのシンボリック型を使います:qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float] — キーが量子ビットインデックスのペア、値が相互作用の重みです。
@qmc.qkernel
def sparse_coupling(
n: qmc.UInt,
edges: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
gamma: qmc.Float,
) -> qmc.Vector[qmc.Bit]:
q = qmc.qubit_array(n, name="q")
# 初期重ね合わせ
for i in qmc.range(n):
q[i] = qmc.h(q[i])
# 指定されたエッジにのみ RZZ 相互作用を適用
for (i, j), weight in qmc.items(edges):
q[i], q[j] = qmc.rzz(q[i], q[j], gamma * weight)
return qmc.measure(q)transpiler.to_circuit()による確認¶
draw()は全パターン(特に複雑な型を伴うitems、if、while)にはまだ対応していません。そのような場合はtranspiler.to_circuit()で全パラメータをバインドした後のトランスパイル済みの回路を確認してください。
edge_data = {(0, 1): 1.0, (1, 2): -0.7, (0, 2): 0.3}
circuit = transpiler.to_circuit(
sparse_coupling,
bindings={"n": 3, "edges": edge_data, "gamma": 0.4},
)
print(circuit) ┌───┐ ┌─┐
q_0: ┤ H ├─■─────────────────────■────────────┤M├───
├───┤ │ZZ(0.4) │ ┌─┐└╥┘
q_1: ┤ H ├─■─────────■───────────┼─────────┤M├─╫────
├───┤ │ZZ(-0.28) │ZZ(0.12) └╥┘ ║ ┌─┐
q_2: ┤ H ├───────────■───────────■──────────╫──╫─┤M├
└───┘ ║ ║ └╥┘
c: 3/═══════════════════════════════════════╩══╩══╩═
1 0 2
edge_dataの3つのエッジのみがRZZゲートを生成します。
if分岐とwhileループ¶
Qamomileは回路途中での測定に続く古典分岐をサポートしています。条件は測定結果(Bit)でなければならず、量子カーネルの引数は使えません。
これはハードウェアレベルの条件付き実行に直接対応します:量子ビットを測定し、その結果に基づいて次の操作を決定します。
測定結果に対するif¶
よくあるパターンとして、ある量子ビットを測定し、その結果に基づいて別の量子ビットにゲートを条件付きで適用します。
@qmc.qkernel
def conditional_flip() -> qmc.Bit:
q0 = qmc.qubit("q0")
q1 = qmc.qubit("q1")
q0 = qmc.x(q0) # |1⟩ を準備
bit = qmc.measure(q0)
# q0 の測定結果に基づいて q1 を条件付きで反転
if bit:
q1 = qmc.x(q1)
else:
pass
return qmc.measure(q1)これはQiskitのif_else命令にトランスパイルされ、実行できます:
exe = transpiler.transpile(conditional_flip)
executor = transpiler.executor()
job = exe.sample(executor, bindings={}, shots=100)
result = job.result()
for value, count in result.results:
print(f" bit={value}: {count} shots") bit=1: 100 shots
q0は |1⟩ として準備されているため、測定結果は常に1となり、q1は常に反転されます。全てのショットで1が返るはずです。
測定結果に対するwhile¶
whileループは測定条件がfalseになるまで繰り返します。これはrepeat-until-successプロトコルに有用です。
@qmc.qkernel
def repeat_until_zero() -> qmc.Bit:
q = qmc.qubit("q")
q = qmc.h(q) # |0⟩ か |1⟩ が 50/50 の確率
bit = qmc.measure(q)
while bit:
# 0 が得られるまで再準備と再測定を繰り返す
q = qmc.qubit("q2")
q = qmc.h(q)
bit = qmc.measure(q)
return bitこれはQiskitのwhile_loop命令にトランスパイルされます。生成された回路構造を確認できます:
exe_while = transpiler.transpile(repeat_until_zero)
qc_while = exe_while.compiled_quantum[0].circuit
print(qc_while) ┌───┐┌─┐
q_0: ┤ H ├┤M├─────────────────────────────
└───┘└╥┘┌───────── ┌───┐┌─┐ ───────┐
q_1: ──────╫─┤ While-0 ┤ H ├┤M├ End-0 ├─
║ └────╥──── └───┘└╥┘ ───────┘
║ ┌────╨────┐ ║
c: 1/══════╩═╡ c_0=0x1 ╞══════╩═══════════
0 └─────────┘ 0
ifとwhileの組み合わせ¶
両方のパターンを組み合わせることができます。以下は測定を繰り返し行い、条件付きで補正ゲートを適用するプロトコルです:
@qmc.qkernel
def measure_and_correct() -> qmc.Bit:
q0 = qmc.qubit("q0")
q1 = qmc.qubit("q1")
q0 = qmc.h(q0)
bit = qmc.measure(q0)
while bit:
# bit が 1 なら q1 に補正を適用
if bit:
q1 = qmc.x(q1)
else:
q1 = q1
# 再準備と再測定
q0 = qmc.qubit("q0_retry")
q0 = qmc.h(q0)
bit = qmc.measure(q0)
return qmc.measure(q1)exe_combined = transpiler.transpile(measure_and_correct)
qc_combined = exe_combined.compiled_quantum[0].circuit
print(qc_combined) ┌───┐┌─┐ »
q_0: ┤ H ├┤M├─────────────────────────────────────────────────────────────────»
└───┘└╥┘┌───────── ┌────── ┌───┐┌──────── ───────┐ ───────┐ »
q_1: ──────╫─┤ ───────┤ If-1 ─┤ X ├┤ Else-1 End-1 ├──── ├─»
║ │ While-0 ┌───┐ └──╥─── └───┘└──────── ───────┘ ┌─┐ End-0 │ »
q_2: ──────╫─┤ ┤ H ├─────╫──────────────────────────────┤M├ ├─»
║ └────╥──── └───┘ ║ └╥┘ ───────┘ »
║ ┌────╨────┐ ┌────╨────┐ ║ »
c: 3/══════╩═╡ c_0=0x1 ╞═════╡ c_0=0x1 ╞══════════════════════════╩═══════════»
0 └─────────┘ └─────────┘ 0 »
«
«q_0: ───
« ┌─┐
«q_1: ┤M├
« └╥┘
«q_2: ─╫─
« ║
«c: 3/═╩═
« 2