FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages

ゼロから作る量子コンピュータ・シミュレータ(5. Pythonインタフェースから全加算器を構成してシミュレート)

量子コンピュータ・シミュレータの実装、骨格となるところは完成しており、あとはいろいろなゲートを挿入するだけとなっている。

まずはアダマールゲート(Hadamard Gate)を実装してみることにする。

Hadamard Gate(Hゲート)の役割

Hadamard Gateを 1量子ビットに適用するためには、以下の行列を適用する。

$$\dfrac{1}{\sqrt{2}}\begin{pmatrix} 1 & 1\\ 1 & -1\\ \end{pmatrix}$$

これだけなら非常に簡単なのだが、これを複数量子ビットに対して適用する場合にはどうしたら良いのだろう?

例えば、以下のように2量子ビットを定義して1ビットにだけHゲートを適用する場合はどのように計算すればよい?

qbit_a = qbit.make_qbit()
qbit_b = qbit.make_qbit()
qbit.h(qbit_a)
# qbit.h(qbit_b)

この場合には、2量子ビットが定義されているのでとりあえず行列の空間としては4要素があり得る。 1ビット目の量子ビットにHゲートが適用されるので、インデックスとしては00⇔01 / 10⇔11 が互いに影響する。 つまり、量子状態ベクトルの要素0と要素1、要素2と要素3の確率が互いに影響される。

一方で、量子ビットの2ビット目に対してHゲートを適用する場合には、インデックスとして00⇔10 / 01⇔11が互いに影響する。 つまり、量子状態ベクトルの要素0と要素2、要素1と要素3の確率が互いに影響される。

f:id:msyksphinz:20181001232715p:plain

これらを実現するためには、以下のように行列式を構成すればよいと思う。

$$\dfrac{1}{\sqrt{2}}\begin{pmatrix} 1 & 1 & 0 & 0\\ 1 & -1& 0 & 0\\ 0 & 0 & 1 & 1\\ 0 & 0 & 1 & -1\\ \end{pmatrix}$$

$$\dfrac{1}{\sqrt{2}}\begin{pmatrix} 1 & 0 & 1 & 0\\ 0 & 1 & 0 & 1\\ 1 & 0 & -1 & 0\\ 0 & 1 & 0 & -1\\ \end{pmatrix}$$

自作量子コンピュータ・シミュレータへのHadamard Gateの実装

という訳で自作量子コンピュータ・シミュレータに実装してみた。とりあえず正しく実装できてるか確認したかったので非常に単純な実装にしてある。

行列の計算とかそのままだ。ライブラリとかちゃんと使え、って感じだが。。。

void Qbits::Hadamard(int idx)
{
  std::vector<std::vector<double>> matrix;
  matrix.resize(m_qbit_size);
  for (size_t i = 0; i < m_qbit_size; i++) {
    matrix[i].resize(m_qbit_size);
    for (size_t j = 0; j < m_qbit_size; j++) {
      matrix[i][j] = 0.0;
    }
  }
  uint64_t c_bitmask = (1 << idx);
  for (size_t idx = 0; idx < m_qbit_size; idx++) {
    if ((idx & c_bitmask) == c_bitmask) {
      matrix[idx][idx^c_bitmask] =  1.0 / sqrtf(2);
      matrix[idx][idx          ] = -1.0 / sqrtf(2);
    } else {
      matrix[idx][idx^c_bitmask] = 1.0 / sqrtf(2);
      matrix[idx][idx          ] = 1.0 / sqrtf(2);
    }
  }

  std::complex<double> *new_comp_amp = new std::complex<double>[m_qbit_size];
  for (size_t v_idx = 0; v_idx < m_qbit_size; v_idx++) {
    new_comp_amp[v_idx] = 0.0;
    for (size_t h_idx = 0; h_idx < m_qbit_size; h_idx++) {
      new_comp_amp[v_idx] += matrix[v_idx][h_idx] * m_comp_amp[h_idx];
    }
  }

  for (size_t idx = 0; idx < m_qbit_size; idx++) {
    m_comp_amp[idx] = new_comp_amp[idx];
  }
}

量子ビットへ適用される行列を表示してみると、以下のようになる。正しく構成できているようだ。

0.707107 0.707107 0.000000 0.000000
0.707107 -0.707107 0.000000 0.000000
0.000000 0.000000 0.707107 0.707107
0.000000 0.000000 0.707107 -0.707107

Hadamard Gateのテスト

という訳で、さっそくテストプログラムを動かしてみる。以下は量子ビットを2つ定義し、1つ目にのみHゲートを適用したあと結果を表示する。 Hゲートは量子ビットを重ね合わせの状態にするので、0と1の状態は1000回観測するとおおよそ半分になるはずだ。

qbit_a = qbit.make_qbit()
qbit_b = qbit.make_qbit()
qbit.h(qbit_a)

result_a = [0, 0]
result_b = [0, 0]

for i in range(0, 1000):
    val = qbit.val(qbit_a)
    result_a[val] = result_a[val] + 1
    val = qbit.val(qbit_b)
    result_b[val] = result_b[val] + 1

print(result_a)
print(result_b)

実行してみると、以下のようになった。qbit_aは重ね合わせの状態なので確率は0と1で半分ずつ。qbit_bは何もゲートを適用していないのでひたすら0のみである。正しく実装できているようだ。

[477, 523]
[1000, 0]