FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

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

フルスクラッチ量子コンピュータ・シミュレータを作る話、骨格となる部分はおおよそ完成し、量子ビットを取り扱う方法を考える。

量子ビットを作成する方法の検討

量子ビットは、1量子ビットPythonの1変数として宣言できるようにしたい。 例えば、以下のようにPythonで記述すると2つの量子ビットが個別に宣言される。

qbit1 = qbit()
qbit2 = qbit()

で、この状態だと2量子ビット存在するので、状態ベクトルとしては 2^{2}のベクトルを内部に保持する必要がある。 さらに量子ビットを追加するとベクトル長は8にまで拡大するので、そのたびに状態ベクトルを拡大する必要がある。

いろいろ考えて、とりあえず今はグローバルに量子ビットを管理するクラスを1つだけ用意し、量子ビットが追加される毎に状態ベクトルを再定義することにした。 そして、量子ビットそのものを示す変数(上記だとqbit1, qbit2)は単なる整数として表現することにした。

PyMethodDef qbit_methods[] = {
...
  { "make_qbit" , (PyCFunction)MakeQbits, METH_VARARGS, "Make Qbits"                     },
...
PyObject* MakeQbits (PyObject* self, PyObject* args)
{
  int new_qbit_idx = g_qbits.AddQbit();
  PyObject *ret = Py_BuildValue("I", new_qbit_idx);
  return ret;
}

量子ビットの操作

例えば、2つの量子ビットに対してCNOTゲートを適用する場合、以下のように記述するイメージだ。

cnot(qbit1, qbit2)

結局のところはqbit1, qbit2は整数で割り当てられたインデックスなので、インデックスに基づいて内部で量子状態を変えているに過ぎない。

PyMethodDef qbit_methods[] = {
...
  { "cnot"      , (PyCFunction)CNot     , METH_VARARGS, "Get Applied CNot"               },
...
PyObject* CNot (PyObject* self, PyObject* args)
{
  int a_idx, b_idx;
  if (!PyArg_ParseTuple(args, "ii", &a_idx, &b_idx))
    return NULL;
  g_qbits.CNot (a_idx, b_idx);

  return Py_BuildValue("i", 0);
}

g_qbits.CNot (a_idx, b_idx)は、行列ベクトルの操作する対象の量子ビット2つ(結局はインデックス)指定して量子状態を変える操作を実行する。

Pythonインタフェースを使って全加算器を作る

という訳で、Pythonインタフェースを整備したので、これを使って全加算器を作る。

量子ビットとしては、入力ビットとしてqbit_a, qbit_b 答えとしてqbit_c0, qbit_c1を用意した。

  • test/test_fulladder.py
qbit_a  = qbit.make_qbit()
qbit_b  = qbit.make_qbit()
qbit_c0 = qbit.make_qbit()
qbit_c1 = qbit.make_qbit()

for b in range (2):
    for a in range (2):
        if a != qbit.val(qbit_a):
            qbit.x(qbit_a)
        if b != qbit.val(qbit_b):
            qbit.x(qbit_b)

        if qbit.val(qbit_c0) == 1:
            qbit.x(qbit_c0)

        print('a + b = s1,s0 => %d + %d = ' % (qbit.val(qbit_a), qbit.val(qbit_b)), end="")

        qbit.ccnot(qbit_a, qbit_b, qbit_c1);
        qbit.cnot (qbit_a, qbit_b);
        qbit.ccnot(qbit_b, qbit_c0, qbit_c1);
        qbit.cnot (qbit_b, qbit_c0);

        print('%d,%d' % (qbit.val(qbit_c1), qbit.val(qbit_c0)))

実際に全加算器を実行しているのは、後半のccnotから始まる4行に過ぎないのだが、それ以外のところでは量子ビットの初期化を行っている。

a=0, b=0 ... a=1, b=1の順番に4種類の加算を実行しているのだが、その度に量子の状態を初期化している。

さっそく実行してみた。正しく計算できている。問題なさそうだ。

$ ./qbit_simulator ../test/test_fulladder.py
a + b = s1,s0 => 0 + 0 = 0,0
a + b = s1,s0 => 1 + 0 = 0,1
a + b = s1,s0 => 0 + 1 = 0,1
a + b = s1,s0 => 1 + 1 = 1,0
f:id:msyksphinz:20180927230554p:plain