AI要約
「15個ごとに処分する栗まんじゅうが1個余るのはいつか?」ショアのアルゴリズムの本質である周期発見問題を身近な例で解説します。素因数分解のためのアルゴリズムと誤解されがちですが、本質である「べき乗の余りの周期を求める」ことに集中します。
はじめに
「結局、量子コンピュータって何が計算できるの?」という質問を1000回くらい受けていまして(オタク特有の誇張表現ではなくマジで)、
模範的な回答は以下の通り。
- 素因数分解ができる(ショアのアルゴリズム)
- 高速検索ができる(グローバーのアルゴリズム)
- 組合せ最適化ができる(QAOA)
- 量子化学計算ができる(VQEなど)
先日、グローバーのアルゴリズムのシリーズは出しました。ただ、グローバーは世間からあまり期待されていないらしい。
ということで、このシリーズでは量子ゲート計算の大本命、「ショアのアルゴリズム」を解説します。まあ、解説といっても難しい数式はまったく出てきません。
このシリーズの学習のねらい
よくある勘違い1
「ショアのアルゴリズムは素因数分解をするためのアルゴリズムだ」
→少し違います。ショアは「べき乗をある数で割った余りの周期を求める」アルゴリズムであり、それを応用することで素因数分解もできる、という立場です。
よくある勘違い2
「ショアのアルゴリズムを使うと素因数分解もできる」
→正しいがニュアンスに注意。ショアのアルゴリズムを使うと「因数分解」ができます。対象の数を「2つの素数の積」で作ったことを前提にすれば、因数分解できた=素因数分解できた、ということになります。
そうなんです。ショアのアルゴリズムを勉強する際、素因数分解を題材にするとノイズが多くて頓死してしまうのです。難しい数学の理論が襲ってきますから。したがって、このシリーズの前半では素因数分解を取り除いた、「べき乗をある数で割った余りの周期を求める問題」を設定して解いていきます。
また、ネット上によくある「15の素因数分解」のサンプルコードは15のために特別に簡略化されている場合が多いため、そのままでは他の数に適用できません。例えば、法が15の計算ではターゲットレジスタは4ビットでいいですが、コントロールレジスタも4ビットとしている例があります。しかし、本来はそれが4ビットで足りるかどうかは未知のため、倍の8ビット確保しておくことがセオリーです。この記事ではそのように、あくまで「未知の答えを求める」スタンスを守ります。
※しかしながら、内部のモジュロ乗算演算(オラクル部)については、加算器や乗算器などのゲートを大量に組み上げる複雑化を避けるため、Python側で算出したユニタリ行列を用いて簡略化しています。ガチンコの量子計算とは少し異なることをご了承ください ( ˘ω˘ )ハイ
問題
おやつの時間です。


栗まんじゅうを食べてもなくならないようにできないかなぁ

できらぁ!




5分ごとに2倍に増えるということは、5時間後には29京個で地球の表面を覆い尽くし、23時間後には観測可能な宇宙の体積を超えるじゃないか…!(超絶IQ)

こりゃまずい!

ねえ!増えた栗まんじゅうを処分する道具を出してよぉぉぉ!

駅前で箱をたくさんもらってきた


この箱に15個ずつ入れて焼却処分しよう。きっちり15個ずつしか処分できないけどね。

すごいや!ちなみに、15個ずつ処分してちょうど栗まんじゅうがなくなることはあるの?

ちょうど0個になることは数学的にありえない

じゃあ、1個だけ余るタイミングはあるかな?それを僕が食べてトドメを刺すよ!

グッドアイデアだね。1→2→4→8個…と増えていったときに、15の倍数+1になるタイミングが知りたいんだね!
この問題、つまりこういうことになります。

一定時間ごとに2倍に分裂する栗まんじゅうが1個ある。15個溜まるごとに処分する場合、手元に1個だけ残るのは何回分裂したあとか?
以下、バイバインに興味がある人向けの参考文献です。
手計算してみる
手計算で倍々にしていって、15で割った余りを見てみましょう。
| 分裂回数 | 個数 | mod 15 |
|---|---|---|
| 0回 | 1 | 1 |
| 1回 | 2 | 2 |
| 2回 | 4 | 4 |
| 3回 | 8 | 8 |
| 4回 | 16 | 1 |
| 5回 | 32 | 2 |
| 6回 | 64 | 4 |
| 7回 | 128 | 8 |
| 8回 | 256 | 1 |
これより、4回分裂して16個になったときに15n+1の条件を満たすので、答えは「4回分裂後」です。
実は、このように「べき乗の数をある数で割った余り」には周期性があります。今回、周期は4で、これが問題の答えと同じになることに注意してください。古典計算では「いつ余りが1に戻ってくるか」は順番に電卓を叩くしかないため、ドデカい周期の数を指定されると膨大な計算時間がかかってしまいます。
それが量子コンピュータを使うことで「同時計算して余りが1になる場所をあぶり出す」みたいなことができるという魂胆です。詳しくは逆量子フーリエ変換とか難しい話になるらしい。
コードで確認
といっても、コード中の各ゲートが何をしているかを解説すると頭から煙が出るので、とりあえず動くコードを紹介します。
import numpy as np
import matplotlib.pyplot as plt
from qiskit_aer import AerSimulator
from qiskit import QuantumCircuit, transpile
from qiskit.circuit.library import UnitaryGate
#コントロール8ビット + ターゲット4ビット
qc = QuantumCircuit(12)
#ターゲットビットの最下位を |1> に初期化
qc.x(8)
#コントロールビットすべてにアダマールを適用
for i in range(8):
qc.h(i)
# --- モジュロべき乗演算 ---
#行列を用いて x -> (x * a_power) mod N の変換ゲートを作る関数
def get_mod_matrix(a_power):
U = np.zeros((16, 16)) #ターゲットが4ビットなので 2^4
for x in range(16):
if x < 15:
y = (x * a_power) % 15
U[y, x] = 1
else:
U[x, x] = 1 #N以上は恒等変換
return U
#コントロールビットすべてにゲートを適用
for i in range(8):
#aを決め、a^(2^i) mod N を計算
a_power = pow(2, 2**i, 15)
U_matrix = get_mod_matrix(a_power)
#行列を制御ゲートに変換
c_U = UnitaryGate(U_matrix).control(1)
#コントロールビット i から、ターゲットレジスタすべてへ適用
qc.append(c_U, [i] + list(range(8, 12)))
qc.barrier()
# --- 逆量子フーリエ変換 (IQFT) ---
#ターゲットビットすべてに適用
for j in range(4):
qc.swap(j, 8 - 1 - j)
#コントロールビットすべてに適用
for i in range(8):
for m in range(i):
qc.cp(-np.pi / (2**(i - m)), i, m)
qc.h(i)
qc.barrier()
#計算
qc.measure_all()
backend = AerSimulator(method='matrix_product_state')
t_qc = transpile(qc, backend) #トランスパイルが必要
result = backend.run(t_qc, shots=1000).result().get_counts()
#解の確認
for r in result:
print(f'{r[::-1]} | {result[r]}/1000')
#解のコントロールビットを集計して確認
all_pattern = {}
for r in result:
pattern = r[::-1][:8][::-1] #上位ビットから並ぶようにひっくり返しておく
if pattern in all_pattern:
all_pattern += result[r]
else:
all_pattern = result[r]
print(all_pattern)
#ピークを確認するためのグラフ
x = range(2**8)
y = [0] * 2**8
for pattern in all_pattern:
y[int(pattern, 2)] += all_pattern
plt.plot(x, y)
plt.xticks(range(0, 2**8 + 1, 32))
plt.show()000000111000 | 66/1000
000000010001 | 57/1000
000000110100 | 64/1000
000000101000 | 69/1000
000000110001 | 56/1000
000000011000 | 68/1000
000000000010 | 63/1000
000000010100 | 64/1000
000000000100 | 58/1000
000000100100 | 68/1000
000000001000 | 66/1000
000000110010 | 67/1000
000000010010 | 69/1000
000000100010 | 47/1000
000000000001 | 63/1000
000000100001 | 55/1000
{'11000000': 253, '10000000': 258, '01000000': 239, '00000000': 250}量子譜はこちら。正直、わからん。

結果の見方です。一行目は「000000111000」が1000回中66回観測されたという意味です。前半8ビットの「00000011」が欲しい部分。ただし左側を下位ビットとして計算したので、左右反転して「11000000」として集計します。
「11000000」は10進数に戻すと192。これが253回ヒットしたことになります。
これらの出力をグラフにしてピークの本数を見やすくしたグラフがこちら。

このグラフはコントロールレジスタが表現できる0~255の整数の間にいくつの共鳴ポイントがあるかを可視化しています。今回、ピークが全体を4分割していることから周期=4と解釈でき、すなわち、答えは「4回分裂後」と求められました。
おわりに
次回は、問題を「21個溜まったら捨てる」に変更してみます。さらに、バイバインの分裂能力を2倍ではなく3倍や4倍に変えて理解を深めます。



