はじめに
量子コンピュータのクラウド実行基盤を自前で触ってみたくて、OQTOPUSの環境をoqtopus-cliでゼロからローカルに構築してみました。quri-parts-oqtopusのようなクライアントSDKからは普段リモートのクラウドに繋いで使うことが多いと思いますが、「本番に繋ぐ前に、まず環境そのものを手元で動かしてみたい」という人向けに、手順と詰まったポイントをまとめておきます。
先にお伝えしておくと、oqtopus-cliの対応OSはLinux/macOSのみで、Windowsはネイティブ非対応です。 私の環境はWindowsだったので、WSL上での構築になりました。この記事はその前提で書いています。
そもそもOQTOPUSとは何か
正式名称はOpen Quantum Toolchain for OPerators & USers。大阪大学・富士通・セック・TISが共同開発している、クラウド型量子コンピュータの基盤(クラウドAPI・ジョブ管理・トランスパイラ・実行エンジン・デバイス管理まで)をフルスタックでオープンソースとして提供するプロジェクトです(大阪大学QIQBのプレスリリースによると「世界最大規模」の量子コンピュータ向けクラウドサービス基盤OSSとのことです)。
量子コンピュータのクラウドシステムは、各ベンダーが自社実装をほぼ非公開のまま提供しているのが実情です。OQTOPUSは、トランスパイラ・マルチプログラミング・誤り緩和といった「QPUに近い層」の実装を標準化してOSSとして公開することで、量子コンピュータシステム開発への参入障壁を下げることを狙っています(arXiv:2507.23165、実際の超伝導量子コンピュータでの運用実績に基づく論文として2025年7月に公開)。
IBM Quantum PlatformやAmazon Braketの多くはクローズドソースで、クラウド側の実装の詳細はユーザーからは見えません。OQTOPUSはクラウドAPIから実行エンジンまでをフルスタックでOSS公開している点がまず大きな違いです。また複数の量子回路を1つの回路にまとめてqubitを効率的に使う「マルチプログラミング」機能も特徴の一つです。
アーキテクチャ
公式サイトによると、OQTOPUSは大きく4層で構成されています。
-
フロントエンド層:
QURI Parts OQTOPUS。ユーザーが書いた回路をOpenQASM3形式に変換してクラウドへ送る -
クラウド層:
OQTOPUS Cloud。ユーザー管理・ジョブ管理・データ管理を担当(--template cloud-localで構築する部分) -
バックエンド層:
OQTOPUS Engine(core/sse_engine/mitigator/estimator/combinerの5サービスから成るモノレポ)・device-gateway・Tranqu。実際の量子計算実行を担当(--template backendで構築する部分。この3つは別リポジトリだがoqtopus backend install allでまとめてインストールされる) -
運用層:
QDash。較正ワークフローと結果管理 -
監視層:
OQTOPUS device monitoring。冷凍機等の単体監視
今回oqtopus-cliで構築するのは、このうちクラウド層とバックエンド層です。両者の関係を図にすると次のようになります。
ポイントは、クライアントが直接叩くのはクラウド層のUser APIだけで、バックエンド層(Engine)はクラウド層のProvider APIに向けて自分から定期的にポーリングしにいく、というプル型の構成になっていることです(②のポーリング先はJOB_REPOSITORY_URLという設定値で決まります。詳しくは後述の「2つの環境が繋がっていることを確認する」を参照)。この記事の後半で、実際にこの2層をローカルに両方構築して繋げてみます。
この記事でやること
-
oqtopus-cliのインストール -
--template cloud-localでのクラウド環境の構築・起動 -
--template backendでのバックエンド(Engine)環境の構築・起動 - 2つの環境が実際に繋がっていることの確認
-
quri-parts-oqtopusクライアントからの接続確認 - backendが正しく起動している証拠・実際にジョブが通っている証拠の提示
- 構築する過程で詰まった2つのポイントとその解決方法
作業の流れを図にすると、こんな感じです。
動作確認環境
- Windows 11
- WSL2(Ubuntu)
- Docker Desktop for Windows(WSL2バックエンド)
Windowsで構築する場合の注意(推奨環境ではありません)
繰り返しになりますが、oqtopus-cliの公式サポートはLinux/macOSのみです。Windowsで動かす場合は次の2段構えが必要になります。
-
WSL(Ubuntu等)上で
oqtopus-cliを実行する。Windowsのコマンドプロンプト/PowerShellから直接は動きません。 -
WSL上でDockerが使える状態にする。
oqtopus backend install engine等がDockerを使うため、WSL単体では足りず、実質的にDocker Desktop for Windows(WSL2バックエンド、対象ディストロとのWSL Integrationを有効化したもの)が必要になります。
つまり「公式にサポートされているLinux環境を、Windows上にWSL2+Docker Desktopでエミュレートして動かしている」状態です。素直にLinuxマシン(あるいはmacOS)が使える方は、そちらで試すことを強くおすすめします。
Docker Desktop側の設定
- Docker Desktop for Windowsをインストールする。
- Settings → General で「Use the WSL 2 based engine」にチェックが入っていることを確認する。
- Settings → Resources → WSL Integration で、
oqtopus-cliを実行するWSLディストロのトグルをONにする。 - 「Apply & Restart」。
- WSL側のターミナルで
docker psがエラーなく返ってくることを確認する(結果が空リストでもOK)。
ここが通らないと、この先のoqtopus backend install系のコマンドがことごとく失敗します。
インストール
WSL側のターミナルで、公式のインストールスクリプトを実行します。
curl -LsSf https://raw.githubusercontent.com/oqtopus-team/oqtopus-cli/main/scripts/install.sh | sh
デフォルトでは~/.local/binに配置されるので、PATHが通っていなければ追加してください。バージョンを指定したい場合は--version、配置先を変えたい場合は--bin-dirをスクリプトへの引数として渡せます。
1. クラウド環境の構築(--template cloud-local)
前述の通り、クライアントSDKが直接繋ぐ相手はクラウド層です。まずこちらから作ります。
oqtopus init my-cloud --template cloud-local
cd my-cloud
oqtopus cloud-local start db # MySQL / MinIO をDockerで起動
oqtopus cloud-local start user # クライアント向けAPI(User API, :8080)
oqtopus cloud-local start provider # Engine向けAPI(Provider API, :8888)
oqtopus cloud-local status
user・provider以外にもadmin(管理API, :8889)・user_signup(サインアップAPI, :8890)・worker(保留ジョブの更新等を行うバックグラウンド処理)がありますが、今回の動作確認にはdb・user・providerがあれば十分です。
2. バックエンド(Engine)環境の構築(--template backend)
続けて、実際に回路を実行するバックエンド層を作ります。
oqtopus init my-backend --template backend
cd my-backend
oqtopus backend install all # engine / tranqu / gateway の最新安定版をインストール
oqtopus backend start all # 依存順にサービスを起動
oqtopus backend status # core, sse_engine, mitigator, estimator, combiner, tranqu, gateway の状態を確認
環境名(my-cloud/my-backendの部分)はDockerのリソース名にも使われるので、小文字・数字・./_/-のみにしておくのが無難です。停止したいときはそれぞれoqtopus cloud-local stop <サービス名>/oqtopus backend stop all。
3. 2つの環境が繋がっていることを確認する
ここが今回一番気になっていたポイントです。クラウド層とバックエンド層は別々のoqtopus initで作った、見た目には無関係な2つのディレクトリです。この2つはどうやって繋がるのかを確認しておきます。
my-backend/config/.envを見ると、バックエンド側がどこをポーリングしにいくかが書かれています。
$ grep JOB_REPOSITORY_URL my-backend/config/.env
JOB_REPOSITORY_URL="http://localhost:8888"
一方my-cloud/config/.env側は、Provider APIのポートがこうなっています。
$ grep PROVIDER_API_PORT my-cloud/config/.env
PROVIDER_API_PORT=8888
両方ともデフォルトで8888になっており、特に手動でリンクさせる設定をしなくても、同じマシン上に両方を素のまま構築するだけで自動的に繋がるようになっていました。 これは正直ちょっと拍子抜けするくらい楽で、事前に「絶対どこかで手動リンクの設定が要るはず」と身構えていたので、意外なポイントでした(別ホストに分けて構築する場合は、当然ここを向き先に合わせて変更する必要があります)。
訂正(公開後に指摘いただいた点): 当初、このJOB_REPOSITORY_URLをポーリングしにいく主体を「gateway」だと書いていましたが、誤りでした。正しくはcoreです。oqtopus-engineのソースコードを確認したところ、
core/src/oqtopus_engine_core/fetchers/repository_job_fetcher.py # Cloud側のジョブをポーリングして取得
core/src/oqtopus_engine_core/fetchers/device_gateway_fetcher.py # coreからgatewayへの疎通確認
core/src/oqtopus_engine_core/steps/device_gateway_step.py # coreのパイプラインからgatewayを呼び出す処理
という構成で、repository_job_fetcher・device_gateway_fetcherはいずれもcore配下にあります。つまり「core」がCloudのProvider APIをポーリングしてジョブを取得し、実機/シミュレータとの実際のやり取りを担う「gateway(device gateway)」へgRPCで実行を依頼する、という一方向の関係で、gatewayが自発的にポーリングしたりcoreを呼んだりすることはありません。上のアーキテクチャ図・後述のシーケンス図はこの向きに修正済みです。
quri-parts-oqtopusからの接続確認
クライアントSDK側は、~/.oqtopusのurlをクラウド層のUser API(ローカルなら:8080)に向けるだけで、リモートのOQTOPUS Cloudに繋ぐときと全く同じコードで動きます(ローカル/リモートを区別する特別な設定は不要です)。
[default]
url=http://localhost:8080
api_token=xxxxxxxxxxxxxxxx
backendが正しく起動している証拠
oqtopus backend statusを実行すると、Engine一式のサービスが全てRunningになっていることが確認できます。
$ oqtopus backend status
core: Running (PID 1290)
sse_engine: Running (PID 1262)
mitigator: Running (PID 1150)
estimator: Running (PID 1183)
combiner: Running (PID 1233)
tranqu: Running (PID 1132)
gateway: Running (PID 1117)
ただ、これだけだとプロセスが立ち上がっているだけかもしれません。実際にquri-parts-oqtopus経由でジョブを投げて、クラウド層→バックエンド層と通って最後まで結果が返ってくることを確認してみます。
今回の構成で、ジョブがどう流れるかを図にすると次のようになります(前述のアーキテクチャ図の①〜③に対応)。
これを踏まえて、一番わかりやすい確認方法として、Bell状態($\frac{|00\rangle+|11\rangle}{\sqrt{2}}$、H→CNOTで作る2量子ビットの最大エンタングル状態)をローカルのqulacsシミュレータデバイスに投げてみました。
from quri_parts.circuit import QuantumCircuit
from quri_parts_oqtopus.backend import OqtopusSamplingBackend
circuit = QuantumCircuit(2)
circuit.add_H_gate(0)
circuit.add_CNOT_gate(0, 1)
backend = OqtopusSamplingBackend()
job = backend.sample(circuit, device_id="qulacs", shots=1000)
result = job.result()
print(f"job_id: {job.job_id}")
print(f"counts: {dict(result.counts)}")
実行結果:
job_id: 06a6f540-80bc-7b22-8000-48c9b1035a53
counts: {0: 479, 3: 521}
0は"00"、3は"11"に対応します(2量子ビットの整数表現)。Bell状態は理想的には"00"と"11"がほぼ半々で現れ、"01"/"10"は出ないはずですが、実際に"00"が479回・"11"が521回とほぼ半々、それ以外は1回も出ておらず、理論通りの結果になりました。実際のOQTOPUS側で発行されたjob_id(UUID)も返ってきており、「クラウド層・バックエンド層それぞれのプロセスが起動しているだけでなく、両者が正しく連携してジョブが最後まで通っている」ことまで確認できました。
詰まったポイント
実際に構築してみて、原因調査に時間がかかったポイントが2つありました。
1. docker psがWSL側で通らない
前述のDocker Desktop側のWSL Integration設定漏れが原因でした。タスクトレイでDocker Desktopが実際に起動しているか、対象ディストロのIntegrationトグルがONになっているかを確認し、それでも直らなければDocker Desktopの再起動、さらにダメならWindows自体の再起動(WSL2カーネル更新直後などに稀に必要)で解消しました。
2. WSL2/Dockerのネットワーク層が不安定になる(Connection reset by peer)
環境を触っているうちに、MySQLコンテナへの新規接続がConnection reset by peerで突然失敗するようになったことがありました。特に大きな操作をした直後というわけでもなく、WSL2⇔Docker間のネットワーク層で起きる一時的な不具合とみられます。
# 実行中のターミナルを一旦すべて閉じてから
wsl --shutdown # PowerShell(管理者)で実行、WSL全体を完全停止
# Docker Desktopをタスクトレイから終了→再起動、起動完了を待つ
oqtopus backend start all # WSL側で環境ディレクトリに入り直して再構築
これで解消しました。再構築後はoqtopus backend statusで全サービスの起動を確認してから、ジョブ実行等を再テストするとよいです。
まとめ
oqtopus-cliを使えば、OQTOPUSのクラウド環境(--template cloud-local)とバックエンド環境(--template backend)をどちらもローカルに構築でき、両者を組み合わせることでゼロから実際にジョブが通るところまで確認できました。ありがたいことに、両者の連携(ポート8888でのポーリング)はデフォルト設定のまま繋がるようになっており、余計な手動設定は不要でした。Linux/macOSであれば素直に動くはずですが、Windows環境の場合はWSL2+Docker Desktopという2段構えが必要になり、公式にサポートされた環境ではない点は留意しておく必要があります。
あらためて、OQTOPUSの良さはどこにあるのか振り返ってみます。IBM Quantum PlatformやAmazon Braketのようなクラウド型量子コンピュータ基盤の多くは、クラウド側の実装がクローズドソースでブラックボックスになっています。OQTOPUSはクラウドAPI・ジョブ管理・トランスパイラ・実行エンジンまでをフルスタックでOSSとして公開しており、今回やったように中身が丸ごとローカルで動かせる・覗けるのが最大の強みだと感じました。トランスパイラやマルチプログラミングといった「QPUに近い層」の実装まで標準化して公開する狙いも、量子コンピュータシステム開発の参入障壁を下げるという意味で、他のクラウド基盤には無い価値だと思います。実機を持っていなくても、こうして手元でクラウド基盤そのものの構造を触って学べるのは、OSSならではの強みですね。
