サーバーレス・アーキテクチャで量子コンピュータ・アプリケーションを運用する方法

こんにちは。@snuffkin と言います。
これは、「[量子コンピューター Advent Calendar 2019](https://qiita.com/advent-calendar/2019/quantum)」の21日目の記事です。

# 身近な量子コンピュータ・アプリケーションを作ろう！

量子コンピュータに関するニュースが活発になってきて、興味を持った方も多いと思います。
特に、AWSによる量子コンピュータのクラウド提供(AWS Braket)は、ITエンジニアにとって量子コンピュータが身近となるきっかけになりそうです。
しかし、AWS Braketを待たずとも、以前からIBM等が量子コンピュータをクラウド提供していることをご存知でしょうか。
そうなんです、**今すぐ量子コンピュータの実機を使ったアプリケーションを作ることができる**んです。

ただ、いざ量子コンピュータに関する勉強をすると、アルゴリズムの理解にはある程度の知識が必要となります。
特に量子の世界の考え方や数学の壁にぶつかって挫折することが多いようです。

実は、**難しいアルゴリズムがなくても量子コンピュータを応用した身近なアプリケーションを作れます**。
最近のクラウド・コンピューティングの発展はめざましく、作成したアプリケーションを公開・運用しやすくなりました。
そこで、AWSを使って量子コンピュータの実機を使ったアプリケーションを作成・公開したのが次のサイトです。

**Quantum Horoscope(量子星占い)**
http://www.quantumcomputer.tokyo/horoscope.html
![site.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13107/4ce454ce-9969-1798-ffec-181c816ec4e7.png)

これは、朝のテレビ番組の占いコーナーに着想を得て作りました。
仕様の概要は次の通りです。

- 毎朝9時に、量子コンピュータ(実機)を実行し、乱数を得る。
- 乱数を元に、各星座の順位付けを行う。
- 順位をサイトに反映する。

このサイトを開発した際に考えたことをまとめたのが、この記事です。

# 設計しよう

まずは、設計から。
どのようなアーキテクチャにするか、決めましょう。

楽に運用したいし、自分がある程度慣れていることもあり、AWSを利用することは確定。
その上で、勉強がてら**サーバーレス・アーキテクチャの構成**を目指すことにしました。
EC2と比べると課金時間が短くなるため、運用費が安くなることを想定しています。

また、**IBMが提供している量子コンピュータを利用**することにしました。
IBMは数年前から量子コンピュータを一般提供をしており、私が使い慣れていることもあります。

これをざっと図にまとめると、次のようなアーキテクチャです。
![archi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13107/911934da-e224-e776-ed9a-27a9831ad503.png)

アーキテクチャの要素を簡単に解説すると、次の通りです。

### サイトの公開方法
- S3上にHTML等のコンテンツを配置し、それを公開する(Webサーバが存在しないため、運用費が安くなります)
- 独自ドメインを取得して利用する(「quantumcomputer.tokyo」にしました)

### 量子コンピュータを実行し、結果にサイトに反映させる
- CloudWatch Eventsを利用し、定期的にアプリケーションを実行する
- アプリケーションはAWS Lambdaを利用し(安い)、AWS LambdaからIBMの量子コンピュータを実行する
- 実行結果をS3上のHTMLに反映する

### アプリケーションの仕様
- 量子コンピュータに問い合わせ、4量子ビットを使って、4ビットの乱数を1024個取得する
- 0000→おひつじ座、0001→おうし座、、、というように星座を割り当てる(星座は12個しかないため、割り当てられない箇所は無視)
- 1024個の乱数のうち、出現頻度が多かった順に1位、2位、、、という順位にする
- 決定した順位をS3上のHTMLに反映する

量子回路が分かる方に向けて、実行した量子回路を掲載します。難しいアルゴリズムは使っておらず、単に乱数を取得するだけです。
![circuit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13107/7bf6f609-570e-6379-577d-1c424f417071.png)

### 利用したフレーワーク

素手で(AWS CLIだけで)開発・デプロイするのは、無茶苦茶しんどいため、Serverless Frameworkを使いました。

Serverless Framework
https://serverless.com/


# 設計上の課題

設計している段階で見えている課題がありました。

1. IBMが実行を完了するまで(数十分レベルで)時間がかかる場合がある
1. AWS Lambdaは実行時間に制限がある(最長15分)

どちらもアプリケーション開発者にはどうしようもない課題で、この課題を踏まえた設計にする必要があります。
「**IBMが実行を完了するまで(数十分レベルで)時間がかかる場合がある**」については、実際に量子コンピュータの実機を使った経験が無いと分からづらいかもしれません。
実はIBMが公開している量子コンピュータは世界中からアクセスでき、実行タスクがキューに積まれて順に実行されます。
そのため、空いている時間は1分もせずに応答が返ってきますが、混雑していると応答が返ってくるまで数十分待つことがあります。

**AWS Lambdaは実行時間に制限がある**ため、AWS Lambdaが応答を待っている間に実行時間制限に引っかかる可能性があります。
制限時間を越えると、AWS Lambda上の処理は強制的にkillされてしまいます。

これら課題への対策としては、量子コンピュータに実行する内容を渡した後、**実行完了するまで定期的にポーリングする**のが有効です。
IBMの量子コンピュータを利用するときのAPIにはポーリングの仕組みがあり、AWSもStep Functionsを使えばポーリングの仕組みを利用でき、AWS Lambdaは実行時間制限に引っかからずに済みます。
この状態遷移を実現するのがStep Functionsです。

具体的には次のような2個のStep Functionsを作成しました。2個作成したのは、独立して実行できるようにするためです。2個に独立していますが、「量子コンピュータから乱数を取得するStep Functions」→「HTML更新用Step Functions」と順に実行されます。


### 量子コンピュータから乱数を取得するStep Functions
![図sfn1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13107/09f1e3f0-0838-d876-c686-ff598f6cb4f5.png)

### HTML更新用Step Functions
![図sfn2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13107/6430150a-bf11-aae2-b1ac-516c6a71ffeb.png)

オレンジのλマークはAWS Lambdaを利用している箇所で、それ以外はStep Functionsの機能で実現できる箇所です。
AWS Lambdaが分割されており、1回のAWS Lambdaの実行時間は数秒です。そのため、量子コンピュータでの実行完了に何分かかろうと、AWS Lambdaの実行時間制限には引っかかりません。

という訳で、先ほどのアーキテクチャ図でAWS Lambdaとまるっと書いた箇所は、実際にはAWS LambdaとStep Functionsを組み合わせた処理にしました。

図の中でDynamoDBが登場しますが、これは量子コンピュータの実行結果を永続化するために使っています。
こうしておくことで、HTML更新に失敗した場合にIBMの量子コンピュータを再実行せずに済みます。
実はIBMの量子コンピュータは1日の実行回数に制限があるため、実行回数を節約できる機能は割と重要だったりします。
また、デバッグにも使えますね。

これで設計上の課題は解決できました。

# 実装上の課題

この設計を元に実装したところ、実装上の課題が出てきました。

1. IBMの量子コンピュータを実行するためのライブラリが巨大すぎて、AWS Lambdaのサイズ制限に引っかかってしまう
2. 量子コンピュータを実行するためのライブラリがマルチプロセスで動作するため、AWS Lambdaの制限に引っかかってしまう

ちょっと悩ましいですね。。。

### IBMの量子コンピュータを実行するためのライブラリが巨大すぎて、AWS Lambdaのサイズ制限に引っかかってしまう

QiskitというPythonのライブラリを利用して量子コンピュータを実行しているのですが、Qiskitには量子コンピュータの動作をシミュレートする機能を始めとして、豊富な機能があります。また、依存するライブラリも多いです。
AWS Lambdaからライブラリを参照する場合はレイヤーと呼ばれる機能を使い、ライブラリをzipに固めるのが一般的です。
ただし、AWS Lambdaには「解凍後のサイズが250MBまで」という制限があります。
Qiskitと依存パッケージをそのまま使うと250MBを越えてしまい、この制限に引っかかってしまいました。

そこで、AWS Lambdaが提供しているライブラリ(numpyやscipy等)をzipに固めないようにすることでサイズ制限をクリアしました。
クライアント側の部分だけで良いので、Qiskit本体から独立した軽量ライブラリが欲しいです。。。

### 量子コンピュータを実行するためのライブラリがマルチプロセスで動作するため、AWS Lambdaの制限に引っかかってしまう

Qiskit 0.10.4を利用していたのですが、プロセスプールを使い、マルチプロセスで実行するようになっています。
一方でAWS Lambdaはマルチプロセス利用を制限しており、マルチプロセス処理を行おうとするとプログラムがエラーとなってしまいます。
これは仕方ないため、Qiskitのコードを少々改造してマルチプロセス処理を行わないようにしました。(分かってしまえば、数行の改造で済みました)

これで実装上の課題は解決でき、動くようになりました。

# 運用上の課題

そこでサイトを公開してみると、すぐに運用上の課題に突き当たりました。

1. 量子コンピュータがメンテナンス中のため、実行に失敗する
1. 実行完了に数時間かかることもあり、Step Functionsの状態遷移が頻発しすぎる

### 量子コンピュータがメンテナンス中のため、実行に失敗する

ときどき量子コンピュータのメンテナンスするようですが、メンテナンス中に実行してもサーバサイドで別サーバに振り直してくれる機能はありません。実行に失敗するだけです。
IBMの量子コンピュータは何台かあり、メンテナンス中かどうかが分かるAPIがQiskitで提供されています。
このAPIを使い、稼働している量子コンピュータを実行するようにして解決しました。

### 実行完了に数時間かかることもあり、Step Functionsの状態遷移が頻発しすぎる

メンテナンス中の量子コンピュータがあると、実行できる量子コンピュータが限られるため、負荷が集中しがちです。
実行が完了するまで、6時間かかることもありました。
そんなに時間がかかると思っていなかったため、Step FunctionsでWaitする部分を10秒にしていました。

Step Functionsは状態遷移の回数で課金するサービスです。
そのため、数時間かかるのに10秒毎に状態遷移するのは、正直言ってやりすぎです。
今回は秒単位の即時性は不要なため、Waitを10秒から60秒に変更しました。

これで運用上の問題も解決し、スッキリしました。

# 運用した結果

実は量子コンピュータが真の乱数を返していないように見えるのですが、その点を除けば特に問題なく順調に動いています。
サーバーレス・アーキテクチャで作ったこともあり、**AWSに毎月支払う額は1ドル前後**です。
EC2とか使っていたら、こうはいかないですね。

いくつかある課題をクリアすれば、量子コンピュータのアプリケーションを安価に運用できることが分かりました。
今回のようなちょっとしたアプリケーションでも良いので、**量子コンピュータのアプリケーションを作る人が増え、身近になるといいな**と思います。

こういうのは楽しいので、量子コンピュータのアプリケーションを作ったら、ぜひ遊ばせてください！
![happy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13107/2dd76ea1-a4c8-19be-6997-998f1c7cf6d4.png)