FPGA開発日記

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

Googleの量子コンピュータフレームワークCirqを使ってみる(6. Cirqのチュートリアルを試す2)

量子コンピュータの勉強を続けており、Cirqの資料をひたすら読み進めている。

以下の資料を読みながら勉強中である。

  • Cir: Tutorial

Tutorial — Cirq 0.1 documentation

一度チュートリアルを読んだのだが、意味が分からずに諦めてしまったのであった。リトライする。

f:id:msyksphinz:20180926011313p:plain
図. Cirqで作成される量子回路の概念図 (https://cirq.readthedocs.io/en/latest/tutorial.html#background-variational-quantum-algorithms より抜粋)

固有値 - Wikipedia

上記のCirqの例では、イジングモデルを作成し、まずは量子ビット間の関係を示す$jr$, $jc$をランダムに決める。 その値に従って量子ビットに対してXゲートを適用している。

つまり、ランダムなので最初の量子回路の形は毎回異なるらしい。量子ビットの位相回転のパラメータを自由に設定しているということになる。

チュートリアルでは、3つの量子ビットの回転角度をそれぞれ$\alpha, \beta, \gamma$としている。 $\alpha=0.1, \beta=0.2, \gamma=0.3$として設定したときの量子回路は以下のようになった。

(0, 0): ───X^0.1───────────@───────────────────────────────────────────────────────────────────────────────────────────M('x')───
                           │                                                                                           │
(0, 1): ───X^0.1───────────┼───────X───@───────X───────────────────────────────@───────────────────────────────────────M────────
                           │           │                                       │                                       │
(0, 2): ───X^0.1───────────┼───────────┼───────────@───────────────────────────@^0.3───────────────────────────────────M────────
                           │           │           │                                                                   │
(1, 0): ───X^0.1───Z^0.2───@^0.3───────┼───────────┼───────@───────────────────────────────────@───────────────────────M────────
                                       │           │       │                                   │                       │
(1, 1): ───X^0.1───────────X───────────@^0.3───X───┼───────┼───────X───@───────X───────────────@^0.3───@───────────────M────────
                                                   │       │           │                               │               │
(1, 2): ───X^0.1───────────────────────────────────@^0.3───┼───────────┼───────────────@───────────────@^0.3───────────M────────
                                                           │           │               │                               │
(2, 0): ───X^0.1───Z^0.2───────────────────────────────────@^0.3───────┼───────────────┼───────────────@───────────────M────────
                                                                       │               │               │               │
(2, 1): ───X^0.1───Z^0.2───────────────────────────────────────────X───@^0.3───X───────┼───────────────@^0.3───@───────M────────
                                                                                       │                       │       │
(2, 2): ───X^0.1───Z^0.2───────────────────────────────────────────────────────────────@^0.3───────────────────@^0.3───M────────

この時にシミュレーションを行った結果、最終的に9つの量子ビットはどのような状態になりうるだろうか。 それをシミュレーションしたのが以下の結果である。 100回シミュレーションを繰り返し、量子ビットの観測結果をヒストグラムで表示してある。

...
circuit.append(one_step(h, jr, jc, 0.1, 0.2, 0.3))
circuit.append(cirq.measure(*qubits, key='x'))
print(circuit)
results = simulator.run(circuit, repetitions=100, qubit_order=qubits)
...

実行結果。

$ python3 cirq_tutorial.py
Counter({0: 83, 4: 4, 32: 3, 256: 2, 64: 2, 128: 2, 8: 1, 2: 1, 144: 1, 320: 1})

この時の量子状態において、エネルギー関数を計算する。 このためにenergy_func()という関数を定義する。

def energy_func(length, h, jr, jc):
    def energy(measurements):
        meas_list_of_lists = [measurements[i:i + length] for i in range(length)]
        pm_meas = 1 - 2 * np.array(meas_list_of_lists).astype(np.int32)
        tot_energy = np.sum(pm_meas * h)
        for i, jr_row in enumerate(jr):
            for j, jr_ij in enumerate(jr_row):
                tot_energy += jr_ij * pm_meas[i, j] * pm_meas[i + 1, j]
        for i, jc_row in enumerate(jc):
            for j, jc_ij in enumerate(jc_row):
                tot_energy += jc_ij * pm_meas[i, j] * pm_meas[i, j + 1]
        return tot_energy
    return energy
def obj_func(result):
    energy_hist = result.histogram(key='x', fold_func=energy_func(3, h, jr, jc))
    return np.sum(k * v for k,v in energy_hist.items()) / result.repetitions

この時のエネルギー関数は以下のようになった。

$ python3 cirq_tutorial.py
[cirq.GridQubit(0, 0), cirq.GridQubit(0, 1), cirq.GridQubit(0, 2), cirq.GridQubit(1, 0), cirq.GridQubit(1, 1), cirq.GridQubit(1, 2), cirq.GridQubit(2, 0), cirq.GridQubit(2, 1), cirq.GridQubit(2, 2)]
Counter({0: 81, 128: 5, 2: 3, 64: 3, 16: 2, 1: 1, 320: 1, 32: 1, 264: 1, 8: 1, 4: 1})
Value of the objective function -0.72

最後に、この3つのパラメータを0.0から1.0まで変化させていき、その時の最小値を求める。このためにはrun_sweep()関数を用いて複数の条件を一気にシミュレーションする。

circuit = cirq.Circuit()
alpha = cirq.Symbol('alpha')
beta = cirq.Symbol('beta')
gamma = cirq.Symbol('gamma')
circuit.append(one_step(h, jr, jc, 0.1, 0.2, 0.3))
circuit.append(cirq.measure(*qubits, key='x'))
results = simulator.run(circuit, repetitions=100, qubit_order=qubits)
print(results.histogram(key='x'))
print('Value of the objective function {}'.format(obj_func(results)))
$ python3 cirq_tutorial.py
...
OrderedDict([('alpha', 1.0), ('beta', 1.0), ('gamma', 0.3333333333333333)]) -4.64
OrderedDict([('alpha', 1.0), ('beta', 1.0), ('gamma', 0.4444444444444444)]) -4.58
OrderedDict([('alpha', 1.0), ('beta', 1.0), ('gamma', 0.5555555555555556)]) -4.82
OrderedDict([('alpha', 1.0), ('beta', 1.0), ('gamma', 0.6666666666666666)]) -4.68
OrderedDict([('alpha', 1.0), ('beta', 1.0), ('gamma', 0.7777777777777778)]) -4.44
OrderedDict([('alpha', 1.0), ('beta', 1.0), ('gamma', 0.8888888888888888)]) -4.7
OrderedDict([('alpha', 1.0), ('beta', 1.0), ('gamma', 1.0)]) -4.5
Minimum objective value is -4.96.