解説
問題 A5 の想定解法では量子ビット数 n に対して量子回路の深さが n+2 であり、この問題を解くことができません。
より効率的な方法を考える必要があります。
以下の 6 量子ビットの例をみてみましょう。
この量子回路は、量子状態 61(∣100000⟩+∣010000⟩+∣001000⟩+∣000100⟩+∣000010⟩+∣000001⟩) を生成します。
以下、量子回路上のバリア 1 を境に分けたブロックごとに解説していきます。
1つ目のブロックでは、1番目の量子ビットに X ゲートを作用させます。
∣000000⟩X(0)∣100000⟩
2つ目のブロックでは、∣100000⟩ と ∣010000⟩ の一様重ね合わせを生成します。
∣100000⟩CRy(θ1,0,1)CX(1,0)63∣100000⟩+63∣010000⟩
3つ目のブロックでは、∣100000⟩ と ∣010000⟩ をさらに分裂させます。
63∣100000⟩+63∣010000⟩CRy(θ2,0,2)CX(2,0)CRy(θ3,1,3)CX(3,1)61∣100000⟩+61∣010000⟩+62∣001000⟩+62∣000100⟩
最後に4つ目のブロックの遷移は次のように表されます。
61∣100000⟩+61∣010000⟩+62∣001000⟩+62∣000100⟩CRy(θ4,2,4)CX(4,2)CRy(θ5,3,5)CX(5,3)61(∣100000⟩+∣010000⟩+∣001000⟩+∣000100⟩+∣000010⟩+∣000001⟩)
なお、各回転角 θi は以下の通りです。
θ1θ2θ4=2arctan(1)=θ3=2arctan(1)=θ5=2arctan(1)
この量子回路の深さは4となるので、はるかに小さい回路の深さを実現できています。
次に、この操作を一般化します。問題となるのは、回転角の定め方です。
6 量子ビットの場合を例として、以下のような木構造を考えます。
各ノード (a,b)(i,j) は、i番目の量子ビットの振幅 b/n のうち a/n を j 番目の量子ビットに分け与える操作を表します。
すなわち、この i 番目を制御ビット、j番目を標的ビットとする制御 Ry ゲートの回転角 θ は以下の連立方程式を解くことで求めることができます。
⎩⎨⎧cos(2θ)=nasin(2θ)=nb−a
この方程式を解くと、0≤θ≤2π の範囲で次の解が得られます。
θ=2arctan(ab−a)
なお、この木構造では親ノード (a,b)(i,j) に対して、上下の子ノードはそれぞれ (⌊⌊b/2⌋/2⌋,⌊b/2⌋)(i,?) と (⌊⌈b/2⌉/2⌋,⌈b/2⌉)(j,?) と表されます。
a=0 のとき、そのような操作は意味をなさないため定義されません。
また、標的ビットのインデックス j は 2 から n までの数を木の根から近い順に各ノードに割り当てていくことで得られます。
以上を元に量子回路を設計することで、量子回路の深さは 2⌈log2n⌉+1 となり、この問題を解くことができます。
実装にあたって、木構造の探索には 幅優先探索 などの手法を用いることができます。
解答例
解答例は以下の通りです。
import math
from qiskit import QuantumCircuit
def solve(n: int) -> QuantumCircuit:
qc = QuantumCircuit(n)
qc.x(0)
count = 1
# queue = [(a, b, control bit of CRy), ...]
queue = [(n // 2, n, 0)]
# breadth first search
while len(queue):
a, b, control = queue.pop(0)
if a == 0:
continue
theta = 2 * math.atan(math.sqrt((b - a) / a))
qc.cry(theta, control, count)
qc.cx(count, control)
queue.append(((b // 2) // 2, b // 2, control))
queue.append((math.ceil(b / 2) // 2, math.ceil(b / 2), count))
count += 1
return qc