賞金総額100万円目指して0から量子プログラミング　episode 0、初期設定編

# 賞金総額100万円のハッカソン開催

ソフトウェア高速化のフィックスターズが賞金総額100万円のハッカソン開催を告知しました。
最優秀賞で40万円。**これは熱い**。
https://amplify.fixstars.com/hackathon00

## サービス開始間もないサービス

ハッカソンの対象になっているのは、2021年2月1日にサービスが開始された量子アニーリングクラウド「Fixstars Amplify」を使ったアプリケーションです。
Amplifyを使っていれば、開発するアプリのテーマは自由。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451580/fd96ba40-2d3d-c842-a216-5eb7f69540db.png" width=80%>

リリースから1ヶ月も経っていないサービスということは、**使い慣れている玄人はほとんど居ない**ということ。これはゼロから挑戦しても結構いい線いけるのでは？

機械学習エブームには乗り遅れたし、量子プログラミングのビッグウェーブには乗っておきましょうか。



え？最終審査当日に予定あるからハッカソンには参加できないって？
・・・まぁ、いいじゃないか（見なかったフリ）

#### 注意

このコンテンツはエンジニアでもない素人がチュートリアルを読みながら書いているチラシの裏のメモです。

---

# 量子アニーリングって何

最近「量子コンピュータ」と呼ばれて話題になっている計算機にはざっくり「量子ゲート方式」と「量子アニーリング方式」があって、Fixstars Amplifyが対象にしているのは量子アニーリングです。D-Wave Systemsってカナダの会社が突然「商用量子コンピュータです」って売り始めて話題になったやつね。

<img src="https://www.dwavesys.com/sites/default/files/DWave_Advantage_System.png" width=50%> <img src="https://www.dwavesys.com/sites/default/files/d_wave_037%20lowres%20fridge.jpg" width=39%>

実のところ、Fixstars Amplifyが現状（2021年2月21日現在）提供しているアニーリングマシン「Fixstars Amplify Annealing Engine（AE）」はGPUを使った計算機で、超電導回路とかイオントラップとか使っている量子コンピュータではありません。

でも、どちらも「イジングマシン」と呼ばれる組合せ最適化問題を解くことに特化した計算機で、Amplify SDKを使ってプログラミングすれば同じコードがそのままD-Wave Systemsの量子コンピュータでも動作するんだとか。実行マシンの設定とアクセストークンの設定書き換えるだけで量子コンピュータが動くらしいよ。D-Waveのマシン使うのに有料の契約が必要だけど。

Amplify AEなら研究や開発用途には無料で使えるし、この使い方を身に着けておけば実質量子コンピュータ（量子アニーリングマシン）も使えるということ。さっそく賞金目指して量子プログラミングをお勉強してみましょう。

## 量子ビットで組合せ最適化？

ちなみに、筆者は大学で固体物理を研究していました。プログラミングや量子コンピュータは分からんけど、量子力学はちょっとわかる。なんで問題が解けるのか、資料読みながらざっくり調べてみました。

量子コンピュータは「0と1とが重なり合った状態」とか説明される量子ビットを使います。ここまでは量子ゲート方式も量子アニーリング方式も一緒。

0とか1というのは人間が割り当てた数字で、実際の装置では磁気スピンの方向などで0と1を表します。スピンが上向きだったら0、下向きだった1といった具合に。で、重なり合った状態というのは「上向きでもあり下向きでもある状態」ということ。よくわからんよね、ほんと。量子力学使って研究してたけど、ほんとよく分からん。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451580/02694c84-04e7-29f6-219a-1588c39fe355.png)


その「0でもあり1でもある」という謎の状態はあんまり安定していなくて、放っておくと0か1のどちらかに落ち着きます。量子アニーリングでは、この落ち着くときにどちらに落ち着いたかで組合せ最適化問題を解きます。

### 未来を知っているかのように振る舞う量子力学

この「どちらに落ち着いたか」が最適化問題の答えになるというのが量子力学の不思議なところ。

上向きになるのも下向きになるのも、どちらも相対的には安定化なんですが、**ある程度重ね合わせの状態が長く保たれていると**まるで「どっちになったほうがより安定になれるのか」という答えを知っているかのように量子ビットの状態はより安定な方に落ち着きます。

一つの量子ビットが安定になるかどうかだけでなく、装置に設定した量子ビット全体の総和として安定になるように、それぞれが上を向くのか下を向くのか選ぶ感じ。その「上」と「下」の組み合わせが、説きたい問題の答え（かその近似解）になっていて、結果的に組合せ最適化問題が解けるとのこと。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451580/dd3dfcfb-4cfd-8462-1ee7-acb0c90076b1.png)

水が低きに流れるように、物理的な状態もエネルギーが低い安定な状態に変化しようとします。不安定な状態から安定な状態に変化するのは分かりますが、「どっちのほうがより安定なのか」はなってみないとわからなそうなものです。

山の頂上にボールを置いても、右に転がるか左に転がるかは分からない。でも、量子の世界では、まるで未来を知っているかのように、より安定なほうに落ち着くんです。不思議。

ちなみに、「**ある程度重ね合わせの状態が長く保たれていると**」というのはとても大事。これを実現するのが難しいから、量子コンピュータを作るのが難しいといってもいいぐらい。

イメージとしては塩の析出を想像してみてください。よりエネルギーの低い安定な状態になろうと塩は個体になります。素早く変化させると粒子として析出しまが、ゆっくり変化すると粒子よりさらに安定な結晶として成長していきます。これも、ある意味で「どっちのほうがより安定か」を分かっているかのように振る舞う物理の不思議。

塩の析出に比べたら量子アニーリングに必要な「重ね合わせ状態を保っておく時間」はものすごく短いけれど、その僅かな時間が得られるかどうかが大事なんです。


---

# 使ってみる

## まずはユーザー登録

https://amplify.fixstars.com/
サイトにアクセスすると、ちょっとスクロールしたところにSDKのインストール方法とアクセストークン（ユーザー登録）の案内があります。とりあえず言われた通りにやりましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451580/88157aab-7bad-182d-48a8-d406c65cce3b.png)

SDKのプログラミング言語はPythonなので、JupyterLabで出力をみながらやってみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451580/6f82001d-1bd6-cc51-8760-ca8d12aabc77.png)

## イジングマシン設定

画面の案内通りにユーザー登録してアクセストークンを入手したら、さっそく軽く動かしてみましょう。

```python

from amplify.client import FixstarsClient

client = FixstarsClient()
client.token = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
client.parameters.timeout = 1000  # 1 sec
```

`client = FixstarsClient()`というのが「Amplify AEを使うよ」という設定。ここを書き換えればD-Waveのマシンが使えるそうです。

`client.token = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"`には先程入手したトークンを設定。間違ってもトークンは自分以外に公開しないように・・・。こちらも、D-Waveのマシンを使う場合はD-Wave Systemsから提供されたトークンを入力するようです。

詳しくはドキュメント参照。
https://amplify.fixstars.com/docs/client.html

## 総和

量子ビットに割り当てる0と1に相当する変数`q`は`BinaryPoly`型で表せるらしい。ほかにも表し方があったけど、とりあえずこれ。

`gen_symbols`で配列として二値変数`q`を生成。とりあえず、全部の変数を足した関数`f`を作ってみました。

※読みやすさのためにimportを繰り返し記述しています。

```python
from amplify import BinaryPoly, gen_symbols

q = gen_symbols(BinaryPoly,9)
f = sum(q)
f
```

JupyterLabなのでfの中身が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451580/41cd4568-738c-5e53-5c59-62de01c41fa2.png)

せっかくJupyterLabなので数式っぽく出力

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451580/2f823918-1088-05dc-0562-a793a9bff4fe.png)

ズラッと並ぶと見づらいですね。数式で書くとこういう状態。

$$
f = \displaystyle\sum_{i=0}^8 q_i 
$$

この関数$f$を評価値として、これが小さくなる変数$q_i$が0 or 1の組合せを探すのがイジングマシンの処理です。

今回は全部足しているだけなので、$q_i$は全部0になります。

```python
from amplify import BinaryQuadraticModel, Solver
from amplify import BinaryPoly, gen_symbols

q = gen_symbols(BinaryPoly,9)
f = sum(q)

model = BinaryQuadraticModel(f)
solver = Solver(client)
result = solver.solve(model)
result[0].values
```

`model = BinaryQuadraticModel(f)`以下は、まぁ、計算するためのお作法みたいなものでしょうか。いや、本当は違うんですけど。

評価関数を論理モデルに変換したりと、解きたい問題の数式をイジングマシンに合わせて変換したりする操作が必要なんです。本来は。

が、Fixstars Amplifyではそのあたりの難しい部分はだいたい自動化されているので、それっぽく書いておくと動いてくれるようです。なんて素人に優しい仕様。

実行結果はこちら。すべての$i$に対して、値は0になっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451580/560159aa-0a75-d26d-5e05-9b0df62a0d1a.png)


## 総乗

これだけだとイジングマシンを使った気になれないので、総乗もやってみましょう。Amplify SDKには補助関数も用意されているようなので、二値変数の総乗なども簡単に記述できます。

```python

from amplify import BinaryPoly, gen_symbols, product

q = gen_symbols(BinaryPoly,9)
f = product(q)
f
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451580/44c3f273-218d-e4e1-6559-de02dab329f5.png)

こちらもJupyterらしく数式表記で出力してみましょうか。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451580/800fa73f-e53e-4fb0-9e23-5f03cad500fc.png)

やっぱりちゃんと数式記号つかった方が好きです。

$$
f = \prod_{i=0}^8 q_i
$$

評価関数$f$は、$q_i$が全て1の場合を除いて常に0になります。
だから、この`f`でイジングマシンを動かすと計算するたびに違う答えが出てきます。

```python
from amplify import BinaryQuadraticModel, Solver
from amplify import BinaryPoly, gen_symbols, product

q = gen_symbols(BinaryPoly,9)
f = product(q)

model = BinaryQuadraticModel(f)
solver = Solver(client)
result = solver.solve(model)
result[0].values
```

1回目
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451580/5448db37-98cf-91bc-6712-bef32b4bfc22.png)

2回目
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451580/c517e779-12aa-1462-c1b2-d798a2495edd.png)

実行するたびに違う答えが返ってくるあたり、ちょっと変わった処理している感があります。これが量子プログラミングなんでしょうか。

「組合せ最適化問題」といいますが、実のところ最適じゃない答えが出てくる場合もあるみたいです。でも、考えようによっては使いやすい性質かもしれません。「移動時間を短くしたい」とか「シフトの偏りを均したい」といった目的がある場合、「計算結果がでません」ってずっとコンピュータを唸らせるプログラムより、「厳密には最適じゃないかもしれないけれど、かなりマシな答えです」と結果が出てくる方が嬉しいこともあるのでは。

なんとなくイジングマシンを動かした気になれたところで、次回はサンプル問題の数独をやってみましょうか。



---
# Fixstars Amplify ハッカソン

https://amplify.fixstars.com/hackathon00

# 次回
episode 1、数独編その1
https://qiita.com/HIROTTA-MAN/items/a94fb38d95cb186ef54b