Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

リソース推定

タグ: tutorial resource-estimation

量子カーネルを実機で実行する前に、必要なリソース(量子ビット数、ゲート数等)を把握しておきたい場合や、そもそも定義した量子カーネルを実行するために必要なリソースを知りたい場合があります。Qamomileのestimate_resources()量子カーネルを実行せずにリソース推定が可能です。具体的な(パラメータ固定の)量子カーネルにも、シンボリック(パラメータ付き)な量子カーネルにも対応しています。

この章では以下を扱います:

# 最新のQamomileをpipからインストールします!
# !pip install "qamomile[qiskit,visualization]"
import qamomile.circuit as qmc

固定量子カーネルのリソース推定

パラメータを持たない量子カーネルに対しては、estimate_resources()は具体的な数値を返します。

@qmc.qkernel
def fixed_circuit() -> qmc.Vector[qmc.Bit]:
    q = qmc.qubit_array(3, name="q")

    q[0] = qmc.h(q[0])
    q[0], q[1] = qmc.cx(q[0], q[1])
    q[1], q[2] = qmc.cx(q[1], q[2])

    return qmc.measure(q)
fixed_circuit.draw()
<Figure size 785.5x256 with 1 Axes>
est = fixed_circuit.estimate_resources()
print("qubits:", est.qubits)
assert est.qubits == 3
print("total gates:", est.gates.total)
assert est.gates.total == 3
print("single-qubit gates:", est.gates.single_qubit)
assert est.gates.single_qubit == 1
print("two-qubit gates:", est.gates.two_qubit)
assert est.gates.two_qubit == 2
qubits: 3
total gates: 3
single-qubit gates: 1
two-qubit gates: 2

シンボリックなリソース推定

量子カーネルに未バインドのパラメータ(例:n: qmc.UInt)がある場合、estimate_resources()SymPy式を返します。特定の値を選ばなくてもコストのスケーリングが分かります。

@qmc.qkernel
def scalable_circuit(n: qmc.UInt, theta: qmc.Float) -> qmc.Vector[qmc.Bit]:
    q = qmc.qubit_array(n, name="q")

    q = qmc.h(q)
    q = qmc.ry(q, theta)

    for i in qmc.range(n - 1):
        q[i], q[i + 1] = qmc.cx(q[i], q[i + 1])

    return qmc.measure(q)
scalable_circuit.draw(n=4, fold_loops=False)
<Figure size 1044.5x336 with 1 Axes>
est = scalable_circuit.estimate_resources()
print("qubits:", est.qubits)
assert str(est.qubits) == "n"
print("total gates:", est.gates.total)
assert str(est.gates.total) == "2*n + Max(0, n - 1)"
print("single-qubit gates:", est.gates.single_qubit)
assert str(est.gates.single_qubit) == "2*n"
print("two-qubit gates:", est.gates.two_qubit)
assert str(est.gates.two_qubit) == "Max(0, n - 1)"
print("rotation gates:", est.gates.rotation_gates)
assert str(est.gates.rotation_gates) == "n"
print("parameters:", est.parameters)
assert set(est.parameters.keys()) == {"n"}
qubits: n
total gates: 2*n + Max(0, n - 1)
single-qubit gates: 2*n
two-qubit gates: Max(0, n - 1)
rotation gates: n
parameters: {'n': n}

出力には、量子ビット数を表すnや総ゲート数を表す2*n + Max(0, n - 1)のようなSymPy式が含まれます。これらは近似ではなく厳密な値です。

Max(0, ...)qmc.range(n - 1)のループ回数に由来します。nが未束縛のためn >= 1を仮定できず、n = 0のときに回数が-1になってしまわないよう0で下限を取っています。具体的なn >= 1を代入すればこのガードは外れるので、後述の合計値はそのまま整数になります。

ResourceEstimateフィールドリファレンス

フィールド説明
est.qubits論理量子ビット数
est.gates.total総ゲート数
est.gates.single_qubit単一量子ビットゲート数
est.gates.two_qubit2量子ビットゲート数
est.gates.multi_qubit多量子ビットゲート数(3量子ビット以上)
est.gates.t_gatesTゲート数
est.gates.clifford_gatesCliffordゲート数
est.gates.rotation_gates回転ゲート数
est.gates.oracle_callsオラクル呼び出し回数(名前別の辞書)
est.parametersシンボル名からSymPyシンボルへの辞書

すべてのフィールドはSymPy式です。固定量子カーネルの場合は通常の整数に評価されます。

.substitute()によるスケーリング分析

シンボリック式は数式を示してくれますが、特定のサイズでの具体的な数値も確認したい場合.substitute()で評価できます:

for n_val in [4, 8, 16, 32]:
    c = est.substitute(n=n_val)
    print(
        f"n={n_val:2d}: {int(c.gates.total):>3} gates total, {int(c.gates.two_qubit):>2} two-qubit"
    )
    assert int(c.gates.total) == 3 * n_val - 1
    assert int(c.gates.two_qubit) == n_val - 1
n= 4:  11 gates total,  3 two-qubit
n= 8:  23 gates total,  7 two-qubit
n=16:  47 gates total, 15 two-qubit
n=32:  95 gates total, 31 two-qubit

ショアのアルゴリズムを回路本体から推定する

実用的な例として、ショアのアルゴリズムの量子部分である位数探索を調べます。 qmc.shor_order_finding()は、底baseと法modulusを受け取り、実行にもリソース推定にも使える1つの量子カーネルを返します。レジスタ幅はmodulus.bit_length()から決まるため、返されたカーネルに人工的なn引数はありません。

order_finding = qmc.shor_order_finding(base=2, modulus=15)
shor_est = order_finding.estimate_resources()

print("logical qubits:", shor_est.qubits)
print("total logical gates:", shor_est.gates.total)
print("estimate quality:", shor_est.quality)

assert shor_est.parameters == {}
assert shor_est.qubits == 21
assert shor_est.gates.total == 3465
assert str(shor_est.quality) == "upper_bound"
logical qubits: 21
total logical gates: 3465
estimate quality: upper_bound

この実装は2*n量子ビットのcountingレジスタを同時に保持しません。1つのphase qubitを測定・リセットして再利用し、それまでに得たビットで半古典的inverse QFTの位相補正を行います。

固定window幅をwとすると、論理量子ビット数は回路本体のallocationから3*n + w + 7になります。

用途
再利用するphase qubit1
モジュラ値を保持するworkレジスタn
モジュラ乗算のaccumulatorn
window lookupの出力レジスタn
lookupのaddressw
carry、vent、overflow、reduction、domain、enable6

既定値はw=2なので3*n + 9です。法15ではn=4となり、推定結果の21量子ビットと一致します。これは外部のコスト式をestimate_resources()へ登録した値ではなく、実行されるqkernel bodyのpeak-live allocationを数えた結果です。

import sympy as sp
from IPython.display import Math, display

symbolic_n, symbolic_w = sp.symbols("n w", integer=True, positive=True)
shor_width_formula = 3 * symbolic_n + symbolic_w + 7
display(Math(rf"N_\mathrm{{qubit}} = {sp.latex(shor_width_formula)}"))
assert shor_width_formula.subs({symbolic_n: 4, symbolic_w: 2}) == shor_est.qubits
Loading...

法15に対するgate内訳も、同じ実行bodyを最後までたどって得られます。

print("single-qubit gates:", shor_est.gates.single_qubit)
print("two-qubit gates:", shor_est.gates.two_qubit)
print("multi-qubit gates:", shor_est.gates.multi_qubit)
print("Toffoli gates:", shor_est.gates.toffoli)

assert shor_est.gates.total == (
    shor_est.gates.single_qubit
    + shor_est.gates.two_qubit
    + shor_est.gates.multi_qubit
)
single-qubit gates: 1645
two-qubit gates: 756
multi-qubit gates: 1064
Toffoli gates: 592

qualityupper_boundなのは、途中測定に基づくclassical feed-forwardの分岐を安全側に数えるためです。また、ここで得られるのはQamomileの論理回路に対する値です。特定デバイスのnative gateへの分解、ルーティング、誤り訂正、magic state生成などは含みません。

なぜgate数はO(n^3)なのか

qmc.modmul_const()は、wビットずつsourceを読み、古典的な倍数をlookupしてaccumulatorへ足します。1回のモジュラ乗算には約n / w個のwindowがあります。各windowで使うripple-carry加算、定数減算、比較、条件付き復元はいずれもO(n)gateです。定数加減算にはGidneyのcarry-venting adderを使い、workレジスタをdirty workspaceとして借りるため、別のn量子ビットレジスタは増えません。

したがって固定wでは、モジュラ乗算1回がO(n^2)、既定の位数探索で行う2*n回のcontrolled multiplicationがO(n^3)です。半古典的inverse QFTのfeed-forwardはO(n^2)なので、全体のleading orderを変えません。lookupの2^w依存まで書けば、おおよそO(2^w n^3 / w)です。

モジュラ乗算単体も同じbodyから推定する

公開primitiveはqmc.modmul_const()です。FTQC算術は具体的な問題インスタンスへspecializeしてから構築するため、ここでも幅4のkernelをそのまま推定します。

@qmc.qkernel
def modular_multiplier() -> qmc.Vector[qmc.Qubit]:
    reg = qmc.qubit_array(4, name="reg")
    return qmc.modmul_const(
        reg,
        multiplier=2,
        modulus=15,
        window_size=2,
    )
window_est = modular_multiplier.estimate_resources()
print("windowed arithmetic qubits:", window_est.qubits)
print("windowed arithmetic gates:", window_est.gates.total)
assert window_est.qubits == 3 * 4 + 2 + 7
assert window_est.gates.total == 1708
windowed arithmetic qubits: 21
windowed arithmetic gates: 1708

standalone版では、無条件実行を表す内部controlがphase qubitの代わりになるため、位数探索全体と同じ3*n + w + 7幅になります。modmul_const()x < modulusの領域では|x> -> |a*x mod modulus>を実行し、領域外の基底状態はunitaryを保つため変更しません。

Ekerå–Håstadの短い指数スケジュール

qmc.ekera_hastad_factoring()は、同程度のビット長を持つ2素数の積を対象とするEkerå–Håstad法のshort discrete logarithm量子段を構築します。Qamomileではm = ceil(n / 2) + 1とし、長さ2*mmの位相スケジュールを順番に測定します。

2つの指数レジスタをcoherentに保持するのではなく、同じphase qubitと算術workspaceを再利用します。そのため幅はShorと同じ3*n + w + 7で、違いはcontrolled modular multiplicationの回数です。返されるVector[Bit]は、先頭2*mビットが長いスケジュール、残りmビットが短いスケジュールで、それぞれlittle-endianです。

以下の法5のインスタンスは、リソース推定を小規模に確認するためのfixtureであり、完全な因数分解例ではありません。実際の因数分解では、2つの素因数を求める対象の合成数を法として与えます。

short_dlp = qmc.ekera_hastad_factoring(
    generator=2,
    modulus=5,
    window_size=2,
)
short_dlp_est = short_dlp.estimate_resources()

print("Ekerå–Håstad logical qubits:", short_dlp_est.qubits)
print("Ekerå–Håstad total logical gates:", short_dlp_est.gates.total)
assert short_dlp_est.qubits == 3 * 3 + 2 + 7
assert short_dlp_est.gates.total == 3685
assert short_dlp.output_types == [qmc.Vector[qmc.Bit]]
Ekerå–Håstad logical qubits: 18
Ekerå–Håstad total logical gates: 3685

まとめ

  • estimate_resources()は実行せずに量子ビット数とゲートコストを算出します。

  • パラメータ付き量子カーネルでは、結果はスケーリングを示すSymPy式になります。qualityで厳密値か上限値かを確認できます。

  • .substitute(n=...)で特定のサイズに代入し、実行可能性を確認できます。

  • FTQC版のShorとEkerå–Håstadは、同じO(n^2)のwindowed modular multiplication bodyと、1つの再利用phase qubitを共有します。

  • 固定window幅では、回路本体から得られる幅は3*n + w + 7、Shorの既定精度でgate数はO(n^3)です。

  • 問題インスタンスにspecializeされたFTQC factoryは、実行可能なbodyから具体的な幅とgate推定値を返します。

次へ実行モデルsample()run()、オブザーバブル、ビット順序について。