Pythonで量子コンピュータを動かしたい

#はじめに
[Googleが量子超越性を実証した](https://mainichi.jp/articles/20191023/k00/00m/020/298000c)とのNewsが出て最近量子コンピュータが何かと話題だったので軽く調べていたのですが、誰でもお試しで量子コンピュータ(後述しますが、量子アニーリング方式の方でGoogleか開発を進めている方式とは異なる)を**誰でもお試しで動かせる方法**があったのでこちらにまとめました。
筆者は本領域の専門家ではないので、説明に誤りがある箇所があるかもしれません。その点はご了承ください。

##参考
量子コンピュータの理解と、実際に量子コンピュータを動かすに当たって下記を参考にさせていただきました。

- [量子コンピュータが変える世界](https://www.amazon.co.jp/dp/4274223728/ref=cm_sw_r_tw_dp_U_x_.hGXDbP05GB67)　寺部 雅能 (著), 大関 真之  (著)　出版社;　オーム社 
- [D-wave Leap](https://cloud.dwavesys.com/leap/login/?next=/leap/)(量子コンピュータを動かすためにこちらで登録)
- [Ocean Software Documentation](https://docs.ocean.dwavesys.com/en/latest/index.html)(量子コンピュータを動かすためのPythonライブラリ)

#量子コンピュータとは何か
量子コンピュータの説明について日経新聞に出ていた説明が完結でわかりやすかったため、
まずはそのまま引用させていただきます。
>
▼量子コンピュータ
物質を構成する原子や電子など、極微の世界で成り立つ物理法則「量子力学」を応用したコンピューター。従来のコンピューターは「0」と「1」のビットという単位で情報を表して計算するが、0と1の並び方によって膨大な量の計算が必要になる。これに対し、量子コンピューターは0と1のどちらでもある「重ね合わせ」という特殊な状態を利用して計算する。この原理を応用すれば、従来は難しかった計算が短時間でできると期待されている。
*2019/10/19 日経新聞　「量子コンピューターとは　グーグルなど参入、開発活発」*

量子コンピュータというのは、**０と１の重ね合わせの状態をもつことのできる量子ビットの存在**が非常に肝になっており、これを使って全ての組み合わせから1番良い組み合わせを瞬時に見つけてくれるマシンが量子コンピュータである、とのことです。

現在開発が進んでいる量子コンピュータは2つの方式があります。

| 方式            | 概要　　　　 |開発ベンダー　　　　 |
|:--------------|:-------------|:-------------|
|量子アニーリング方式|組み合わせ最適化のタスク処理に特化した方式|D-wave Systemsなど|
|量子ゲート方式 |従来のコンピュターのような何でも計算できる汎用的な方式|Google,IBMなど|

現在企業による実証実験が進んでいるのは量子アニーリング方式のマシン方です。
2011年にカナダのD-wave Systemsという会社が量子アニーリングマシンの商用販売を開始し様々な企業で実証実験が行われています。

日本の企業だと[リクルートコミュニケーションズが量子コンピュータの広告最適化への適用](http://dwavejapan.com/recruit-communications-d-wave-collaborate-apply-quantum-computing-marketing-advertising-communications-optimization/)を目指して研究開発を行なっているようです。

今回、Pythonで動かすのはD-wave Systemsの量子アニーリングマシンです。**2019年11月現在、登録するだけで誰でも1分間無料で量子アニーリングマシンを動かすことができます。**

#準備
##量子コンピュータを動かす準備
[こちら](https://cloud.dwavesys.com/leap/login/)のサイトからまずは利用者登録を行います。名前とメールアドレスと職業程度の情報を入力するだけで登録可能で、クレジットカード情報とかも不要です。

![スクリーンショット 2019-11-09 9.44.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395230/fa994c0f-8857-afcd-057b-b7aade36c573.png)

ログインするとまずトップにこのような画面が表示されます。一番左が、各種説明ムービーでこの量子アニーリングマシンによって何ができるか説明がなされています。真ん中は量子コンピュータのデモを見ることができますが、お試しの1分間の中から時間を消費されてしまうので注意です。一番右にはPythonライブラリで量子アニーリングマシンを動かす方法の説明が書かれています。

![スクリーンショット 2019-11-04 18.33.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395230/f0dee9be-1839-f75c-5a46-3fd564b8bced.png)

スクロールするとこのようなダッシュボードも表示されています。
こちらには残りどの程度の時間、量子コンピュータを動かすことができるかが表示されています。

![スクリーンショット 2019-11-04 18.33.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395230/44fd61ec-d114-39db-3d80-9c7e7ab6e7ed.png)

使用するとこんな感じで時間が減っていきます。

![スクリーンショット 2019-11-09 9.53.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395230/075634b6-1a3e-464c-ac5e-6a3292177c97.png)

##Pythonライブラリの準備
ライブラリですが、普通にpipでインストールすることが可能です。
ただ自分がいつも使用している環境では何故かインストール時にエラーが頻発したため、Dockerでmachine　learning用のイメージからコンテナを立ち上げでそこでインストールを行いました。

```
pip install dwave-ocean-sdk
```
こちらのインストールが完了したら下記コマンドを実行してください。

```
dwave config create
```

すると下記が上から順に表示されていくのですが、基本的にはそのままEnterを押していただき、
「Authentication token」のところには自分のAPItokenを入れてください。
自分のAPItokenはマイページトップからコピーができます。

```
Confirm configuration file path [/home/jane/.config/dwave/dwave.conf]:
Profile (create new) [prod]:
API endpoint URL [skip]:
Authentication token [skip]: 自分のAPItokenが記載
Default client class (qpu or sw) [qpu]:
Default solver [skip]:
Configuration saved.
```
こちらで事前準備は完了です。
正常に接続できていることを確認するためには下記コマンドを実行します。

```
dwave ping
```

このような感じ表示されればOKです。

```
Using endpoint: https://my.dwavesys.url/
Using solver: My_DWAVE_2000Q

Wall clock time:
 * Solver definition fetch: 2007.239 ms
 * Problem submit and results fetch: 1033.931 ms
 * Total: 3041.171 ms

QPU timing:
 * total_real_time = 10493 us
 * anneal_time_per_run = 20 us
 * post_processing_overhead_time = 128 us
 * qpu_anneal_time_per_sample = 20 us
```

#量子コンピュータでタスクを解く
それでは量子コンピュータを用いて実際にタスクを解いていきます。
##量子コンピュータで解くタスク
今回は[APIリファレンスのサンプル問題](https://docs.ocean.dwavesys.com/en/latest/examples/scheduling.html#scheduling)として用意されている、
制約条件付きのスケジューリングタスクを解いていきます。
###問題
MTGを開催する条件として下記規則が定められています。
この規則を反しない形でMTGを開催できる条件を網羅的に抽出することを試みます。
**規則**
- 定時内かつオフィスに居る時は、直接MTGに参加しなければならない(電話会議はだめ)
- 定時内に開かれるMTGには必ず参加しなければならない(強制参加)
- 定時外のMTGは必ず電話会議で行わなければならない
- 定時外のMTGは30分を超えてはならない

##タスクを解く
###問題の変換
まず、上記の条件をコンピュータに読み込ませることができるように0と1で変換していきます。

- `time`が1の時は「定時内」0の時は「定時外」
- `location`が1の時は「オフィス内」0の時は「オフィス外」
- `length`が1の時は「MTG時間が30分以下」0の時は「MTG時間が30分以上」
- `mandatory`が1の時は「強制参加」0の時は「任意参加」 

それでは規則に対して上記の0,1変換を落とし込んだ関数を作成します。

```python
def scheduling(time, location, length, mandatory):
    if time:                                 # 定時内
        return (location and mandatory)      # オフィス内で強制参加
    else:                                    # 定時外
        return ((not location) and length)   # 30分以下の電話会議
```
この関数からライブラリを用いて制約条件を作成します。

```python
import dwavebinarycsp
csp = dwavebinarycsp.ConstraintSatisfactionProblem(dwavebinarycsp.BINARY)
csp.add_constraint(scheduling, ['time', 'location', 'length', 'mandatory'])
```
さらにこちらをイジイングモデル(二次形式最小化問題)という形式に変換します。
**量子コンピュータに問題を解かせるためにはこの形式に問題を変換することが必須**となります。
下記引数で指定している`min_classical_gap`ですが公式のサンプルコードでは指定されていません。デフォルトのままだとうまくいかず、こちらで指定しましたが原因は調査してもわかりませんでした...分かる方いらっしゃれば教えていただけると幸いです。

```python
bqm = dwavebinarycsp.stitch(csp, min_classical_gap=2.1)
print(bqm)
```
```
BinaryQuadraticModel({'a': -1.0, 'aux0': -2.0, 'aux1': 0.0, 'b': 2.0, 'c': 1.0, 'aux2': -1.0}, {('aux0', 'a'): 1.0, ('aux1', 'a'): 1.0, ('aux1', 'aux0'): -1.0, ('b', 'a'): -1.0, ('b', 'aux0'): -1.0, ('b', 'aux1'): -1.0, ('c', 'a'): 0.0, ('c', 'aux0'): -1.0, ('c', 'aux1'): -1.0, ('c', 'b'): 1.0, ('aux2', 'a'): 1.0, ('b', 'aux2'): -1.0, ('c', 'aux2'): 1.0}, 0.0, Vartype.SPIN)
```
上記の出力は数式的にはこちらに対応しています。
$\sum_i^N q_ix_i + \sum_{i<j}^N q_{i,j}x_i  x_j$

###量子コンピュータに問題を解かせる

```python
from dwave.system.samplers import DWaveSampler
from dwave.system.composites import EmbeddingComposite
sampler = EmbeddingComposite(DWaveSampler(endpoint='https://cloud.dwavesys.com/sapi', token='自分のAPItokenを記載', solver='DW_2000Q_2_1'))
```
量子コンピュータにサンプリングを要求します。
サンプリングされた結果は確率的なので、1つではなく多くのサンプルを要求することで複数の「最適」な解を呼び出し、「最適」でない解に落ち着くことを防ぎます。以下では引数で5000個のサンプルを要求しています。
※ここの工程で量子コンピュータの使用時間を消費します。

```python
response = sampler.sample(bqm, num_reads=5000)
#energyの値が最も小さい解が最適解
min_energy = next(response.data(['energy']))
```
それではサンプリング結果を見てみます。

```python
total = 0
for sample, energy, occurrences in response.data(['sample', 'energy', 'num_occurrences']):
    total = total + occurrences 
    if energy == min_energy:
        time = '定時内' if sample['time'] else '定時外'
        location = 'オフィス' if sample['location'] else '電話会議'
        length = '30分以下の' if sample['length'] else '30分より長い'
        mandatory = '強制参加' if sample['mandatory'] else '任意参加'
        sub = str(sample['mandatory'])
#         print("{}: During {} at {}, you can schedule a {} meeting that is {}::{}".format(occurrences, time, location, length, mandatory, sub))
        print(" {}は{}で、{}MTGを{}で開催することができます。".format(time, location, length, mandatory))
```

```
定時外は電話会議で、30分以下のMTGを任意参加で開催することができます。
定時内はオフィスで、30分以下のMTGを強制参加で開催することができます。
定時内はオフィスで、30分より長いMTGを強制参加で開催することができます。
定時内はオフィスで、30分より長いMTGを強制参加で開催することができます。
定時内はオフィスで、30分より長いMTGを強制参加で開催することができます。
定時外は電話会議で、30分以下のMTGを強制参加で開催することができます。
定時内はオフィスで、30分より長いMTGを強制参加で開催することができます。
```
重複している解が複数ありますが、全て制約条件を満たした解を抽出できています。

#Next
取り敢えず量子コンピュータを動かしてはみたものの、QUBO行列やらイジイングモデルやら、わからないところが一杯あったので時間があれば勉強していきたいと思います。
また今回はテストで実施したデータ量が少なかったのと、この量子コンピュータ自体もクラウドで接続したものなので、中々その話題の計算スピードを実感するには至りませんでした。この領域は日進月歩で進んでいく領域だと思うのでちゃんと追っていきたいです。