D-Wave SystemのOceanを使った足し算




[D-Wave System社](http://dwavejapan.com)が提供する量子アニーリングでイジングやQUBO問題を解くPython製パッケージであるOceanを説明して、簡単な足し算のプログラミングを例に解説します。

# 1. 量子コンピューターのSDK

量子コンピュータのSDKは実機別に公開されてますが、それらをまとめて汎用的なもので提供してくれようとしてるものもあります。

主な量子コンピューターSDKの一覧

|方式|名称|提供元|開発言語|
|:-----|:------|:------|:------|
|量子アニーリング|[Ocean](https://ocean.dwavesys.com)|D-Wave System|Python|
|量子アニーリング|[Wildqat](http://mdrft.com/wildqat/)|MDR社|Python|
|量子ゲート|[QISKit](https://qiskit.org)|IBM|Python|
|量子ゲート|[QDK](https://www.microsoft.com/en-us/quantum/development-kit)|Microsoft|Q#|
|量子ゲート|[Cirq](http://github.com/quantumlib/Cirq)|Google|Python|
|量子ゲート|[ProjectQ](https://projectq.ch/)|チューリッヒ工科大学|Python|
|量子ゲート|[Forest](https://www.rigetti.com/)|Rigetti Computing|Python|
|量子ゲート|[Blueqat](http://mdrft.com/blueqat/)|MDR社|Python|

# 2. イジングモデルとは、QUBOとは

量子コンピューターには主に量子ゲート方式と量子アニーリング方式の２種類があり、量子ゲート方式は汎用の量子計算が可能であるが、量子アニーリング方式はイジング・QUBO問題に見られる組み合わせ最適化問題に特化した方式です。
現在の量子アニーリング方式では、D-WaveSystemsが2048量子ビットまで実現できている。
どちらの方式もハミルトニアンを構築するまでは同じだが、量子ゲート方式では、解きたい問題のハミルトニアンを量子アルゴリズムに従い、ビット反転演算Xや位相反転演算Z、制御NOTゲートCNOTなどの量子ゲートを使って組み立てる必要がある。一方、量子アニーリング方式は、解きたい問題のハミルトニアンをイジングモデルやQUBOモデルのバイナリ2次モデルに変換すれば、あとは量子アニーラーに任せるだけで、そのハミルトニアンの最小エネルギーを求めるので複雑な量子ゲートを組み立てる必要はなく、非常に簡単で短い量子コードで作成することができます。
イジングモデルやQUBOモデルの詳しい説明は以下をご参考ください。
[量子アニーリング、イジングモデルとフレームワーク](https://qiita.com/YuichiroMinato/items/e6952fec1a9965156873)
[QUBOとイジングモデルは、具体的にどう対応するのか](https://qiita.com/gyu-don/items/d8536b719f343cc365d6)

# 3. Oceanとは

Oceanは、D-Wave Systemが提供する量子アニーリングでイジングやQUBO問題を解くPython製パッケージです。
ライセンスは、Apache License 2.0で提供されています。

D-Wave SystemのGPUを使うとお金がかかってしまうのですが、Oceanを使えば、D-Wave SystemのGPU（量子コンピューター）とローカルマシン上のCPU/GPU（古典的コンピューター）を簡単な切り替えで使うことができるので、どんなものか試したかったり、初期段階の開発や試験運用では無料でできます。これに比べて、同じ量子アニーリングでも富士通のデジタルアニーラーは全て有料の商用サービスなので、こういったアプローチがとりずらく、この結果、興味のある人々へまずは試してもらうという機会を逃してしまうことになります。

# 4. Oceanのインストール

早速、Oceanをインストールしてみましょう。
Pythonパッケージなので、pipコマンドを使えば簡単にインストールされます。
Macの場合はターミナル、Windowsの場合はAnacondaプロンプトで以下のコマンドを実行するだけです。

```shell:
$ pip install dwave-ocean-sdk
Collecting dwave-ocean-sdk
  Downloading https://files.pythonhosted.org/pac
   ：
uccessfully installed dimod-0.7.9 dwave-cloud-client-0.4.16 dwave-neal-0.4.3 dwave-networkx-0.6.6 dwave-ocean-sdk-1.0.3 dwave-qbsolv-0.2.9 dwave-system-0.5.4 dwavebinarycsp-0.0.9 homebase-1.0.1 minorminer-0.1.6 ortools-6.7.4973 penaltymodel-0.15.3 penaltymodel-cache-0.3.2 penaltymodel-mip-0.1.3
```

念の為にインストールできたかも確認しましょう。

```shell:
$ python -c 'import dimod; print(dimod.ExactSolver())'
<dimod.reference.samplers.exact_solver.ExactSolver object at 0x107abbc50>
```

詳細については本家のマニュアルを参照してください。
https://docs.ocean.dwavesys.com/en/latest/overview/install.html

# 5. 足し算（1+2）をQUBOモデルにしてみよう

今回は$ 1 + 2 = x $となるようなxを求めます。

QUBOにおいて足し算は上記右辺から左辺を引いてその最小値を求めることに相当しますので、$ x - ( 1 + 2 ) = 0$の目的関数を最小にするような変数xを求める問題になります。

$\\begin{align} f(x) &= \\{x-(1+2)\\}^2 \\\\ &= (x-3)^2 \\end{align}$

QUBOモデルでは、目的関数$f(x)$には、バイナリ変数$x\\in\\{0,1\\}$が使われ、スカラー係数$ a_i,b_{i,j} $を用いたスカラー形式にできる。

$\begin{align} f(x) &= \sum_{i}{a_i}{x_i} + \sum_{i<j}b_{ij}x_{i}x_{j} \end{align}$

QUBOは、行列の対角要素$ Q_{i,j} $を一次係数とし、非対角係数$ Q_{i,j} $を二次係数とした行列形式で表せれる。

$\begin{align} f(x) &= \sum_{i}{Q_i}{x_i} + \sum_{i<j}Q_{ij}x_{i}x_{j} = \sum_{i<j}x_{i}Q_{ij}x_{j} \end{align}$

一方、xは量子ビットを使って、
$\\begin{align} x = x_0 + 2x_1 \\end{align}$
という二進数表記ができますので、さらにこれを上記のコスト関数に代入すると、

$\begin{align} 
f(x) &= \\bigl(x_0 + 2x_1\\bigl)^2 - 6\\bigl(x_0+2x_1\\bigl) + 9 \\\\
&= x_0^2 + 4{x_0}{x_1} + 4x_1^2 - 6x_0 -12x_1 + 9 \end{align}$

と展開されます。ここで、QUBOモデルの変数$x\\in\\{0,1\\}$はバイナリ値{0,1}を取るので、二乗の項はすべて指数がとれます。

$\begin{align} x_0^2 = x_0 ,\quad x_1^2 = x_1 \end{align}$

これにより、目的関数はオフセット値9とスカラー形式の目的関数

$\begin{align} f(x) &= \sum_{i}{a_i}{x_i} + \sum_{i<j}b_{ij}x_{i}x_{j}+9 \\\\ &=-5x_{0}-8x_{1}-4x_{0}x_{1}+9 \end{align}$

となります。これをQUBOの行列表記にすると、下記のようになります。

$\begin{align} f(x) &= \sum_{i<j}{x_i}Q_{ij}x_{j} + 9 \\\\ &= 
\begin{bmatrix}
x_0 & x_1 
\end{bmatrix}
\begin{bmatrix}
-5 & 4 \\\
0 & -8 
\end{bmatrix}
\begin{bmatrix}
x_0 \\\
x_1 
\end{bmatrix}+9
\end{align}$

# 6. Oceanパッケージのサンプラー種類とコード例

ここから先はOceanパッケージの各サンプラーを紹介し、コード例も紹介します。
問題が初歩過ぎてつまらないかもしれませんが、どの辺が変わるかだけでも参考にしていただければ幸いです。

| 種類|ツール|サンプラー                         |概要 |
|:----|:-----|:--------------------------------|:------------------|
|古典的| dimod|[ExactSovler](#61-exractsolver)               |簡単な問題で正確な解を求めるためのサンプラー|
|古典的| dimod|[RandomSampler](#62-randomsampler)             |ランダムに探索するサンプラー|
|古典的| dimod|[SimulatedAnnealingSampler](#63-simulatedannealingsampler) |シミュレーテッド・アニーリング（焼きなまし法）で求めるサンプラー|
|古典的| neal|[SimulatedAnnealingSampler](#64-nealsimulatedannealingsampler)  |シミュレーテッド・アニーリングで、dimodよりう詳細な設定が行えるサンプラー|
|量子実機| dwave.system|[DWaveSampler](#65-dwavesampler)     |D-Wave System社量子コンピューターのハードウェア実機（QPU）を使って解を求めるサンプラー。有償利用となります。|
|量子実機| dwave.cloud|[Solver](#66-d-wave-cloud-solve)            |上記より細かい制御が可能なもの。こちらも有償利用となります。|
|古典的/量子実機| dimod|[QBSolv](#67-qbsolv)            |大きなQUBO問題を解くソルバー。デフォルトは古典的ソルバーで探索するが、QPUへの切り替えもできる。|

## 6.1. ExractSolver

簡単な問題で正確な解を求めたい時はこちらを利用します。

QUBOモデルの行列Qの値の$\begin{align}\begin{bmatrix}
-5 & 4 \\\
0 & -8 
\end{bmatrix}\end{align}
$と、オフセット値の9を用いて、以下のように設定します。

```python:
from dimod import *
Q = {(0, 0):-5,
     (0, 1): 4,
     (1, 1): -8
    } #QUBOモデル行列Q
b = BinaryQuadraticModel.from_qubo(Q, 9.0)
r = SimulatedAnnealingSampler().sample(b)
for s, e in r.data(['sample','energy']):
    print(s,'\t E = ', e)
```

[実行結果]

```terminal
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
```

最小エネルギー$E=0$を与えるバイナリ変数は、$x_0=1,x_1=1$なので次のように計算ができます。

$x=x_0+2x_1=1+2 \\times 1=3$

## 6.2. RandomSampler

これはランダムに探索するサンプラーです。開発時の試験運営に利用します。

```python:
from dimod import *
Q = {(0, 0):-5,
     (0, 1): 4,
     (1, 1): -8
    }
b = BinaryQuadraticModel.from_qubo(Q, 9.0)
r = RandomSampler().sample(b) # ここだけ修正
for s, e in r.data(['sample','energy']):
    print(s,'\t E = ', e)
```

[実行結果]

```terminal
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 0, 1: 1} 	 E =  1.0
{0: 1, 1: 0} 	 E =  4.0
{0: 1, 1: 0} 	 E =  4.0
{0: 0, 1: 0} 	 E =  9.0
{0: 0, 1: 0} 	 E =  9.0
{0: 0, 1: 0} 	 E =  9.0
{0: 0, 1: 0} 	 E =  9.0
```

一番エネルギーの小さい（$E=0$）バイナリ変数は、$x_0=1,x_1=1$なのであってます。


## 6.3. SimulatedAnnealingSampler

これは焼きなまし法で近似解を求めるサンプラーです。開発時の試験運営に利用します。

```python:
from dimod import *
Q = {(0, 0):-5,
     (0, 1): 4,
     (1, 1): -8
    }
b = BinaryQuadraticModel.from_qubo(Q, 9.0)
r = SimulatedAnnealingSampler().sample(b) # ここだけ修正
for s, e in r.data(['sample','energy']):
    print(s,'\t E = ', e)
```

[実行結果]

問題が簡単なので全部あってますね。

```terminal
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
```

## 6.4. neal.SimulatedAnnealingSampler

これは焼きなまし法で解を求めるサンプラーです。開発時の試験運営に利用します。
Simodツールに比べると細かいパラメータの設定が可能です。

### 線形

```python:
from dimod import *
import neal
Q = {(0, 0):-5,
     (0, 1): 4,
     (1, 1): -8
    }
b = BinaryQuadraticModel.from_qubo(Q, 9.0)
r = neal.SimulatedAnnealingSampler().sample(b,
     seed=1111,
     sweeps=20,
     beta_schedule_type='linear') # ここだけ修正
for s, e in r.data(['sample','energy']):
    print(s,'\t E = ', e)
```

[実行結果]

```terminal
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
```

### 非線形

```python:
from dimod import *
import neal
Q = {(0, 0):-5,
     (0, 1): 4,
     (1, 1): -8
    }
b = BinaryQuadraticModel.from_qubo(Q, 9.0)
r = neal.SimulatedAnnealingSampler().sample(b,
     seed=1111,
     sweeps=20,
     beta_schedule_type='geometric') # ここだけ修正
for s, e in r.data(['sample','energy']):
    print(s,'\t E = ', e)
```

[実行結果]

```terminal
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
{0: 1, 1: 1} 	 E =  0.0
```

## 6.5 DWaveSampler（有料）

D-Wave Systemの量子アニーリングの実機を使う方法です。
ただし、日本からは個人で使わせてもらえる手段がわかりませんのでコード例だけ記述します。

```python:
from dimod import *
from dwave.system.samplers import *
Q = {(0, 0):-5,
     (0, 1): 4,
     (1, 1): -8
    }
b = BinaryQuadraticModel.from_qubo(Q, 9.0)
r = DWaveSampler().sample(b) # ここだけ修正
for s, e in r.data(['sample','energy']):
    print(s,'\t E = ', e)
```

[実行結果]

（有料機能を使える方は結果を教えてくれたら嬉しいです。）

## 6.6 D-Wave Cloud Solve（有料）

D-Wave Systemの量子アニーリングの実機を使う方法です。
ただし、日本からは個人で使わせてもらえる手段がわかりませんのでコード例だけ記述します。

```python:
from dimod import *
from dwave.system.samplers import *
from dwave.system.composites import EmbeddingComposite

Q = {(0, 0):-5,
     (0, 1): 4,
     (1, 1): -8
    }
b = BinaryQuadraticModel.from_qubo(Q, 9.0)

token = 'ここにあなたのTokenを記載' # ここだけ修正
r = EmbeddingComposite(DWaveSampler(token=token)).sample(b)
for s, e in r.data(['sample','energy']):
    print(s,'\t E = ', e)
```

[実行結果]

```terminal
{0: 1, 1: 1} 	 E =  0.0
```
*2019/6/29 有料機能（D-wave 実機）にて検証済み

## 6.7. QBSolv

大きな問題をサブ問題に分割するソルバーです。
デフォルトはローカルでの古典的で実行されますが、D-WaveのQPUを利用することも可能です（その場合は有償）。

```python:
from dimod import *
from dwave_qbsolv import QBSolv
Q = {(0, 0):-5,
     (0, 1): 4,
     (1, 1): -8
    }
b = BinaryQuadraticModel.from_qubo(Q, 9.0)
r = QBSolv().sample(b) # ここだけ修正
for s, e in r.data(['sample','energy']):
    print(s,'\t E = ', e)
```

[実行結果]

```terminal
{0: 1, 1: 1} 	 E =  0.0
```

Oceanはサンプラーを変えるだけで、すぐに切り替えができるのでとても便利ですね。

## 7. （参考）Wildqat SDK

日本を代表する量子コンピューター企業にMDR社があります。MDR社は量子アニーリングのSDKとしてWildqatを公開しています。また、ゲートモデル用ではBlueqatも公開してます。WildqatでもQUBOを使って解を求めることができるか確認してみました。

WildqatもPython製なのでpipにより簡単にインストールができます。

```shell:
$pip install wildqat
```

コード例は以下になります。オフセットの設定が見当たりませんでしたが、最小コストを求めるのには変わらないのでQUBOだけ設定して解を求めます。

```python:
import wildqat as wq
a = wq.opt()
a.qubo = [[-5,4],[0,-8]]
a.sa()
```

[実行結果]
正しい結果（$x_0,x_1$）が出力されました。

```terminal:
[1, 1]
```

## 8. 便利なユーティリティ関数

Oceanには便利なユーティリティ関数が存在するので紹介します。

|ユーティリティー関数|概要|
|:------------------------------|:---|
|ising_to_qubo(h,J,offset=0.0)|イジング問題をQUBO問題へ変換|
|qubo_to_ising(Q,offset=0.0)|QUBO問題をイジング問題へ変換|
|ising_energy(sample,h,J,offset=0.0)|イジングモデルのサンプルからエネルギーを算出|
|qubo_energy(sample,Q,offset=0.0)|QUBOのサンプルからエネルギーを算出|

QUBOをイジングモデルに変換して実行した例

```python:
Q = {(0, 0): -5.0, (0, 1): 4.0, (1, 1): -8.0}
print(qubo_to_ising(Q, 9.0))
```

イジングモデルへの変換結果

```terminal:
({0: -1.5, 1: -3.0}, {(0, 1): 1.0}, 3.5)
```

イジングモデルからバイナリ二次モデルを生成して、解を求める。

```python:
b = BinaryQuadraticModel({0: -1.5, 1: -3.0}, {(0, 1): 1.0}, 3.5,SPIN)
r = ExactSolver().sample(b)
for s, e in r.data(['sample','energy']):
    print(s,'\t E = ', e)
```

あたり前ですが、実行結果はQUBOの時と同じです。

```terminal:
{0: 1, 1: 1} 	 E =  0.0
{0: -1, 1: 1} 	 E =  1.0
{0: 1, 1: -1} 	 E =  4.0
{0: -1, 1: -1} 	 E =  9.0
```

OceanのBinaryQuadraticModelクラス（バイナリ二次モデル）にも、便利なメソッドが揃ってましたので、こちらも紹介します。

|メソッド|概要|
|:---|:---|
|from_ising(h,offset])|イジングモデルからBQMへ変換|
|from_qubo(Q[,offset])|QUBOモデルからBQMへ変換|
|from_numpy_vectors()|NumPyベクトルからBQMへ変換|
|from_numpy_matrix(mat)|NumPy配列からBQMへ変換|
|from_pandas_dataframe(df)|pandasからBQMへ変換|
|from_coo(obj[,type])|座標からBQMへ変換する|
|to_ising()|BQMからイジングモデルへ変換|
|to_qubo()|BQMからQUBOモデルへ変換|
|to_numpy_vectors(])|BQMからNumPyベクトルへ変換|
|to_numpy_matrix([])|BQMからNumPy配列へ変換|
|to_pandas_dataframe()|BQMからpandasデータフレームへ変換|
|to_coo([fp,typer])|BQMから座標フォーマットへ変換|
|to_networkx_graph([])|BQMからNetworkxグラフへ変換|

# 参考文献

https://www.slideshare.net/masayukiminato/jan-2018-85514793
[Ocean量子アニーリング入門 中山茂](https://www.amazon.co.jp/dp/B07KZWH6BC)