🐈

Qulacs使ってみた報告

2022/07/12に公開

Google Colaboratory で Qulacs使ってみた報告.
https://github.com/qulacs/qulacs

Qulacsとは

高速な量子回路シミュレータ.Python・C・C++に対応.

チュートリアル

親切な解説(日本語)がある.とはいえこの記事でも逐次説明する.
https://dojo.qulacs.org/ja/latest/notebooks/3.1_Qulacs_tutorial.html

インストール

以下のコマンドでQulacsをColab上でインストールできる.

!pip install qulacs
## Google Colaboratory/(Linux or Mac)のjupyter notebook環境の場合にのみ実行してください。
## Qulacsのエラーが正常に出力されるようになります。
!pip3 install wurlitzer

量子フーリエ変換回路

n bit の量子フーリエ回路をQulacsを用いて書いてみる.

量子フーリエ変換の回路は以下の図で書かれる.

量子フーリエ変換の回路図

ここで R_j は以下を満たす作用素.

R_j=\begin{pmatrix}1&0\\0&e^{2\pi i/2^j}\end{pmatrix}

量子フーリエ変換の回路は2ビット目以降に対しては n-1 bit の量子フーリエ変換を実行しているだけなので,再帰的に回路を構成することが可能.とはいえ QuantumCircuit から QuantumCircuit を作る機能は用意されていないようなので,以下のようにする.

ゲート関数

ゲートを作成する関数を作り,後のゲート作成を自動化する.

from qulacs import QuantumState
from qulacs.gate import DenseMatrix
from cmath import exp
import math

def gate_R(param,target,control):
  gate = DenseMatrix(target, [[1,0],[0,exp((0 + 1j) * math.pi /2**(param-1))]])
  gate.add_control_qubit(control,1)
  return gate

重要ポイントは以下の通り.

  • 行列で表されるゲートを作るには DenseMatrix(target,matrix) を使う.
    • target は対象の制御対象の量子ビットの番号.(番号は0から始まる)
    • matrix はゲートの行列.
  • コントロールビットを持っている場合は .add_control_qubit(control,X) を用いて素の(コントロールビットを持たない)ゲートに追加する.
    • control は制御ビットの番号.
    • X は制御ビットがどちらのときに適用するか(X=1 なら 1のときに作用する).

ゲート追加関数

ゲート追加関数 add_n_fourier(circuit,circuit_size,target_number) は,

  • 量子回路 circuit
  • 回路の量子ビット数 circuit_size
  • 現在追加対象の量子ビット target_number

以上を元に circuit に n 次元フーリエ変換回路を付加する関数.

ちなみに,1次元フーリエ変換回路は H ゲートを作用させるだけである.

def add_n_fourier(circuit,circuit_size,target_number):
  circuit.add_H_gate(target_number);
  if target_number == circuit_size - 1 : 
      return
  for i in range(circuit_size - target_number - 1):
    circuit.add_gate(gate_R(i+2,target_number,i+target_number+1))

  add_n_fourier(circuit,circuit_size,target_number+1)

実際に使うときには target_number には 0 を入れる.

動作確認

以下のコードで正しく動いているかチェックする.テストコードが汚すぎる・・・

from qulacs import QuantumCircuit

n = 2
circuit = QuantumCircuit(n)
add_n_fourier(circuit, n, 0)

state0 = QuantumState(n)
state1 = QuantumState(n)
state2 = QuantumState(n)
state3 = QuantumState(n)

state0.set_computational_basis(0b00)
state1.set_computational_basis(0b01)
state2.set_computational_basis(0b10)
state3.set_computational_basis(0b11)

print(state0.get_vector())
print(state1.get_vector())
print(state2.get_vector())
print(state3.get_vector())

circuit.update_quantum_state(state0)
circuit.update_quantum_state(state1)
circuit.update_quantum_state(state2)
circuit.update_quantum_state(state3)

print("実行後")

print(state0.get_vector())
print(state1.get_vector())
print(state2.get_vector())
print(state3.get_vector())

重要ポイントは以下の通り.

  • QuantumCircuit(n)n 量子ビットの量子回路を作る.
  • QuantumState(n)n 量子ビットの量子状態(ベクトル)を作る.
  • state.set_computational_basis(0b00) で量子状態 state\ket{00} にする.
  • state.get_vector() で量子状態 state をベクトルとして表示する.
  • circuit.update_quantum_state(state) で量子状態 state に量子回路 circuit を適用する.

この実行結果が以下の通り.上が通過前,下が回路通過後の状態である.

[1.+0.j 0.+0.j 0.+0.j 0.+0.j]
[0.+0.j 1.+0.j 0.+0.j 0.+0.j]
[0.+0.j 0.+0.j 1.+0.j 0.+0.j]
[0.+0.j 0.+0.j 0.+0.j 1.+0.j]
実行後
[0.5+0.j 0.5+0.j 0.5+0.j 0.5+0.j]
[ 0.5+0.j -0.5+0.j  0.5+0.j -0.5+0.j]
[ 5.000000e-01+0.j   3.061617e-17+0.5j -5.000000e-01+0.j
 -3.061617e-17-0.5j]
[ 5.000000e-01+0.j  -3.061617e-17-0.5j -5.000000e-01+0.j
  3.061617e-17+0.5j]

e-17e^{-i \pi} の計算の時に出た端数なので,実質 0 である.量子回路を使わない版が以下.テストコードがもっと汚くなる

array0 = [1,0,0,0]
array1 = [0,1,0,0]
array2 = [0,0,1,0]
array3 = [0,0,0,1]

def calculator(array,k):
  sum = 0
  sum += array[0] * exp(-2 * math.pi * (0+1j) * k * 0/4)
  sum += array[1] * exp(-2 * math.pi * (0+1j) * k * 1/4)
  sum += array[2] * exp(-2 * math.pi * (0+1j) * k * 2/4)
  sum += array[3] * exp(-2 * math.pi * (0+1j) * k * 3/4)
  return sum/2

sum00,sum01,sum02,sum03 = calculator(array0,0) , calculator(array0,1),calculator(array0,2),calculator(array0,3)
sum10,sum11,sum12,sum13 = calculator(array1,0) , calculator(array1,1),calculator(array1,2),calculator(array1,3)
sum20,sum21,sum22,sum23 = calculator(array2,0) , calculator(array2,1),calculator(array2,2),calculator(array2,3)
sum30,sum31,sum32,sum33 = calculator(array3,0) , calculator(array3,1),calculator(array3,2),calculator(array3,3)
print(sum00,sum01,sum02,sum03)
print(sum10,sum11,sum12,sum13)
print(sum20,sum21,sum22,sum33)
print(sum30,sum31,sum32,sum33)

これの実行結果は以下の通り.これはフーリエ変換後の値しかprintしていない.

(0.5+0j) (0.5+0j) (0.5+0j) (0.5+0j)
(0.5+0j) (3.061616997868383e-17-0.5j) (-0.5-6.123233995736766e-17j) (-9.184850993605148e-17+0.5j)
(0.5+0j) (-0.5-6.123233995736766e-17j) (0.5+1.2246467991473532e-16j) (2.755455298081545e-16-0.5j)
(0.5+0j) (-9.184850993605148e-17+0.5j) (-0.5-1.8369701987210297e-16j) (2.755455298081545e-16-0.5j)

こっちも端数 e-16 とか e-17 を実質 0 とすれば量子回路の実行結果とだいたい一致している.

結論

Qulacsを使って n 次元フーリエ変換回路を自動で生成し,その回路が正しい値を返していることを確認した.

Discussion