import math
from qiskit import QuantumCircuit, QuantumRegister
def inv_mod(a: int, L: int) -> int:
def extended_euclidean(a: int, b: int) -> tuple[int, int, int]:
# when b == 0:
if b == 0:
return a, 1, 0
# recursive step
gcd, x1, y1 = extended_euclidean(b, a % b)
# update x and y
x = y1
y = x1 - (a // b) * y1
return gcd, x, y
_, x, _ = extended_euclidean(a, L)
return x % L
def beki(n: int, a: int, L: int) -> list[int]:
result = [a]
for _ in range(n - 1):
result.append(result[-1] ** 2 % L)
return result
def qft(n: int) -> QuantumCircuit:
qc = QuantumCircuit(n)
for i in reversed(range(n)):
qc.h(i)
for j in reversed(range(i)):
qc.cp(math.pi / 2 ** (i - j), j, i)
for i in range(n // 2):
qc.swap(i, n - i - 1)
return qc
def mcadd(n: int, a: int, c: int) -> QuantumCircuit:
qc = QuantumCircuit(n + c)
qc.compose(qft(n), qubits=range(n), inplace=True)
for i in range(n):
theta = 2 * math.pi * a * 2**i / 2**n
if c == 0:
qc.p(theta, i)
elif c == 1:
qc.cp(theta, n, i)
else:
qc.mcp(theta, list(range(n, n + c)), i)
qc.compose(qft(n).inverse(), qubits=range(n), inplace=True)
return qc
def mcadd_mod(n: int, a: int, L: int, c: int) -> QuantumCircuit:
qc = QuantumCircuit(n + 1 + c)
qc.compose(mcadd(n + 1, 2**n - L + a, c), qubits=range(n + 1 + c), inplace=True)
qc.x(n)
qc.compose(mcadd(n, -(2**n - L + a), c + 1), qubits=range(n + 1 + c), inplace=True)
qc.x(n)
qc.compose(mcadd(n, 2**n - a, c + 1), qubits=range(n + 1 + c), inplace=True)
qc.compose(mcadd(n + 1, a, c), qubits=range(n + 1 + c), inplace=True)
return qc
def cswap(n: int) -> QuantumCircuit:
qc = QuantumCircuit(2 * n + 1)
for i in range(n):
qc.cswap(2 * n, i, i + n)
return qc
def solve(n: int, m: int, a: int, L: int) -> QuantumCircuit:
x, y = QuantumRegister(n), QuantumRegister(2 * m + 1)
qc = QuantumCircuit(x, y)
qc.h(x)
qc.x(y[0])
lst = beki(n, a, L)
a_inv = inv_mod(a, L)
lst_inv = beki(n, a_inv, L)
lst_minus_inv = [(-v) % L for v in lst_inv]
for i in range(n):
b = lst[i]
for j in range(m):
qc.compose(mcadd_mod(m, b, L, 2), qubits=[*y[m:], x[i], y[j]], inplace=True)
b = 2 * b % L
qc.compose(cswap(m), qubits=[*y[: 2 * m], x[i]], inplace=True)
b = lst_minus_inv[i]
for j in range(m):
qc.compose(mcadd_mod(m, b, L, 2), qubits=[*y[m:], x[i], y[j]], inplace=True)
b = 2 * b % L
return qc
今回のコンテストの問題案の原案には最後に以下のような問題 D1, D2 がありました。
ジャッジシステムの都合で出題には至りませんでしたが、ショアのアルゴリズムについての理解を深めるために問題と解説を残しておきます。
開く
∣C5⟩=2m1k=0∑2m−1∣k⟩∣gkmodL⟩∣C5⟩ の変形を考えたいですが、まずは以下の A, B, C, D の式を準備します。
G を以下のように定義します。
任意の x に対し
G∣xmodL⟩=∣gxmodL⟩⋯A突然ですが、0≤s<r に対し、∣us⟩ を次のように定義します。
∣us⟩=r1k=0∑r−1exp(r−2πiks)∣gkmodL⟩⋯Bこのとき ∣us⟩ について以下の C, D が成り立ちます。
r1s=0∑r−1∣us⟩=∣1⟩⋯CG∣us⟩=exp(r2πis)∣us⟩⋯DC の証明:
r1s=0∑r−1∣us⟩=r1s=0∑r−1r1k=0∑r−1exp(r−2πiks)∣gkmodL⟩=k=0∑r−1(r1s=0∑r−1exp(r−2πiks))∣gkmodL⟩=∣1⟩※最後の等号は以下により成り立つ。
k=0 のとき
r1s=0∑r−1exp(r−2πiks)=r1s=0∑r−11=1k=0 のとき
r1s=0∑r−1exp(r−2πiks)=r11−exp(r−2πik)1−exp(r−2πikr)=r11−exp(r−2πik)1−1=0D の証明
G∣us⟩=r1k=0∑r−1exp(r−2πiks)G∣gkmodL⟩=r1k=0∑r−1exp(r−2πiks)∣gk+1modL⟩=exp(r2πis)r1k=0∑r−1exp(r−2πi(k+1)s)∣gk+1modL⟩=exp(r2πis)r1k′=0∑r−1exp(r−2πik′s)∣gk′modL⟩=exp(r2πis)∣us⟩準備ができました。
A, B, C, D を使って ∣C5⟩ を変形します。
∣C5⟩=2m1k=0∑2m−1∣k⟩∣gkmodL⟩=2m1k=0∑2m−1∣k⟩Gk∣1⟩=2m1k=0∑2m−1∣k⟩Gkr1s=0∑r−1∣us⟩=2m1k=0∑2m−1∣k⟩exp(r2πisk)r1s=0∑r−1∣us⟩=2m1k=0∑2m−1exp(r2πisk)∣k⟩r1s=0∑r−1∣us⟩少し厳密ではない説明になってしまいますが、r1∑s=0r−1∣us⟩の部分を無視すると、
2m1k=0∑2m−1exp(2m2πi(2ms/r)k)∣k⟩となっており、これに対し IQFT を作用させて観測した値をλとすると、λ≈r2ms
よって、2mλ≈rs
ここで、r だけでなく s もわからない事に注意してください。
しかし、この情報だけでも r の候補を絞ることができます。
例えば、
2mλ=81923413
だったとします。
連分数展開により以下のように近似計算できます。
81923413=0+2+2+2+170+411111≈0+2+2+2+0111=125こうして、r=12 と推定できます。