0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OQTOPUS local backendでマルチプログラミングジョブを試してみた

0
Last updated at Posted at 2026-08-06

はじめに

前回の記事でOQTOPUSのローカル環境(oqtopus-cliでクラウド層を構築し、quri-parts-oqtopusから接続する構成)を組んだので、今回はその環境を使って「マルチプログラミングジョブ」を実際に試してみた話を書く。

環境

前回記事と同じく、Windows + WSL2 + Docker Desktop 上に oqtopus-cli で構築した cloud-local テンプレートの環境が起動している状態から始める。

quri-parts-oqtopus のバージョンは以下の通り。

$ pip show quri-parts-oqtopus
Name: quri-parts-oqtopus
Version: 1.1.4

デバイス一覧を見ると、ローカル環境には qulacs(16qubitシミュレータ)や SVSim(39qubitシミュレータ)、モック的なQPUデバイスがいくつか登録されている。今回は qulacs を使う。

from quri_parts_oqtopus.backend.devices.device import OqtopusDeviceBackend

backend = OqtopusDeviceBackend()
for d in backend._client.list_devices():
    print(d.raw.device_id, d.raw.device_type, d.raw.n_qubits)
01927422-...  QPU        64
Kawasaki      QPU        64
qulacs        simulator  16
SC            QPU        64
SVSim         simulator  39

マルチプログラミングジョブとは

複数の(比較的qubit数の少ない)量子回路を1つの物理デバイス上にまとめて配置し、1回のジョブとして同時に実行する仕組み。実機のqubit数に対して1つの回路が使うqubit数が少ない場合、デバイスを分割して複数ユーザー/複数回路を相乗りさせることでスループットを上げよう、というモチベーション。

ソースを読むと、sample()/sample_qasm() の中に実装が入っている。

# quri_parts_oqtopus/backend/jobs/sampling.py (抜粋)
if job_type is None:
    if isinstance(program, list):
        job_type = "multi_manual"
    else:
        job_type = "sampling"
        program = [program]

回路をlistで渡すだけで、自動的に job_type="multi_manual" になる。ジョブ種別としては oqtopus_client.rest.models.jobs_job_type.JobsJobTypeSAMPLING / ESTIMATION / MULTI_MANUAL / SSE の4種類が定義されていて、MULTI_MANUAL がまさにこれ。名前が "manual" なのは、おそらく「ユーザーが自分で複数回路を1本のジョブにまとめて送る」方式という意味だろう。

試してみる

2qubitのBell状態と3qubitのGHZ状態、2種類の回路を用意する。

from quri_parts.circuit import QuantumCircuit
from quri_parts_oqtopus.backend import OqtopusSamplingBackend

SHOTS = 2000

# 回路A: 2qubit Bell状態
circuit_a = QuantumCircuit(2)
circuit_a.add_H_gate(0)
circuit_a.add_CNOT_gate(0, 1)

# 回路B: 3qubit GHZ状態
circuit_b = QuantumCircuit(3)
circuit_b.add_H_gate(0)
circuit_b.add_CNOT_gate(0, 1)
circuit_b.add_CNOT_gate(1, 2)

backend = OqtopusSamplingBackend()

まずは比較のためそれぞれ単独で実行する。

job_a = backend.sample(circuit_a, device_id="qulacs", shots=SHOTS)
print(job_a.result().counts)
# => {3: 1011, 0: 989}

job_b = backend.sample(circuit_b, device_id="qulacs", shots=SHOTS)
print(job_b.result().counts)
# => {0: 1020, 7: 980}

理論通り、Bell状態は 00/11がほぼ半々(0と3)、GHZ状態は 000/111がほぼ半々(0と7)になっている。

次に、この2つの回路をリストにしてまとめて投げる。

job_multi = backend.sample([circuit_a, circuit_b], device_id="qulacs", shots=SHOTS)
result = job_multi.result()

print(result.counts)
# => {3: 487, 28: 547, 0: 505, 31: 461}

print(result.divided_counts)
# => {0: Counter({0: 1052, 3: 948}), 1: Counter({7: 1008, 0: 992})}

結果を読み解く

counts(合成分布)のキーは、回路ごとの測定結果のビット列を連結した値になっている。今回は「回路A(2bit) | 回路B(3bit) << 2」という形で、リストの先頭(回路A)が下位ビット、後続(回路B)が上位ビットに配置されているようだ。

合成値 2進数(5bit) 回路A(下位2bit) 回路B(上位3bit)
0 00000 0 (00) 0 (000)
3 00011 3 (11) 0 (000)
28 11100 0 (00) 7 (111)
31 11111 3 (11) 7 (111)

回路Aと回路Bはそれぞれ独立に測定されているので、4通りの組み合わせがほぼ均等(2000shots/4 ≈ 500)に出現している。これは「2つの回路をまとめて実行しているが、測定結果としては独立」ということを裏付けている。

そして divided_counts を見ると、インデックス0が回路A、インデックス1が回路B(渡したリストの順序と対応)に対応しており、それぞれの分布は単独実行時の結果({3: 1011, 0: 989} / {0: 1020, 7: 980})とほぼ一致した。つまり「まとめて実行しても、個々の回路の測定結果はちゃんと分解して取り出せる」ことを確認できた。並べて比較するとこの通り。

単独実行とマルチプログラミング実行の分布比較

物理qubitへの実際のマッピングを見る

ここまでは「回路ごとの測定結果」の話だったが、そもそも2つの回路はデバイス上のどの物理qubitに割り当てられたのかも気になったので調べてみた。

job.job_info を覗くと、transpile_result の中に virtual_physical_mapping というキーがあり、ここに仮想qubit(回路上のqubit番号)と物理qubit(デバイス上の実際のqubit)の対応表が入っている。

info = job.job_info
print(info["transpile_result"]["virtual_physical_mapping"])

  "qubit_mapping": {"0": 13, "1": 15, "2": 14, "3": 12, "4": 9},
  "bit_mapping":   {"0": 0,  "1": 1,  "2": 2,  "3": 3,  "4": 4}
}

combined_program(2回路を1本のOpenQASMにまとめたもの)を見ると分かるが、仮想qubitの0,1が回路A(Bell)、2,3,4が回路B(GHZ)に対応している。つまり:

仮想qubit 所属回路 物理qubit
0 回路A q0 13
1 回路A q1 15
2 回路B q0 14
3 回路B q1 12
4 回路B q2 9

これをqulacsデバイス(16qubit)のトポロジー(list_devices()device_infoに含まれるqubitの座標・カプラー接続情報)の上に重ねて図示すると、以下のようになる。

qulacsデバイス上でのマルチプログラミングジョブの物理qubit割り当て

回路A(青)は物理qubit 13-15、回路B(オレンジ)は物理qubit 9-12-14 という、デバイス上で互いに重ならない領域に配置されている。ちゃんと2つの回路が異なる物理qubit領域に「相乗り」していることが視覚的に確認できた。

まとめ

  • quri-parts-oqtopussample() に回路のリストを渡すだけで、マルチプログラミングジョブ(job_type="multi_manual")が試せる
  • 合成された counts は回路ごとのビット列を連結したものになっており、divided_counts で個々の回路の分布に分解できる
  • virtual_physical_mappingtranspiled_programを読めば、2つの回路が実際にデバイス上のどの物理qubitへ、互いに重ならないように配置されたかまで追跡できる

補足

  • ソースコードを読む限り、estimatorについてはマルチプログラミング未対応のようでした。
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?