Pulser入門 - Pasqal版・中性原子量子コンピュータSDKを触ってみよう
はじめに
前回のBloqade入門では、QuEra社のSDKを使って中性原子量子コンピュータを体験しました。
実は、中性原子量子コンピュータのSDKはもう一つあります。
それがPulser(パルサー)。フランスのPasqal社が開発したオープンソースのPython SDKです。
この記事では、ストロンチウム原子が大好きな著者が、
- PulserとBloqadeって何が違うの?
- Pulserの基本的な使い方
- 同じ問題を両方で解いて比較
を解説します。
第1章: PulserとBloqade、何が違う?
2大SDKの比較
中性原子量子コンピュータには、2つの主要なSDKがあります。
| 項目 | Pulser | Bloqade |
|---|---|---|
| 開発元 | Pasqal(フランス) | QuEra(アメリカ) |
| 実機 | Pasqal QPU | QuEra Aquila |
| クラウド | Pasqal Cloud | AWS Braket |
| 設計思想 | 物理寄り・低レベル | 抽象化・高レベル |
| エミュレータ | QuTiP連携 | 独自実装 |
どっちを使うべき?
正直、どっちでもいいです。
ただ、以下のような傾向があります:
- Pulser向き: 物理をしっかり理解したい人、細かいパルス制御をしたい人
- Bloqade向き: 素早くプロトタイピングしたい人、AWS環境がある人
両方触ってみて、肌に合う方を選ぶのがおすすめです。
Pasqal社について
Pasqalは2019年にフランスで設立された量子コンピューティング企業です。
- 共同創業者の一人は2022年ノーベル物理学賞受賞者のAlain Aspect氏
- 欧州最大級の中性原子量子コンピュータを開発中
- 2024年に1000原子を達成、10,000量子ビットを目指すロードマップを発表
第2章: Pulserをインストールする
インストール
pip install pulser
これだけ。Bloqadeと同じくらい簡単。
確認:
import pulser
print(pulser.__version__)
最初のプログラム:原子を配置する
Pulserでは、まず「Register」(レジスタ)を定義します。これが原子の配置です。
import numpy as np
from pulser import Register
# 原子を3つ、一直線に配置
coords = [(0, 0), (6, 0), (12, 0)] # 座標 (μm)
reg = Register.from_coordinates(coords, prefix="atom")
# 可視化
reg.draw()
BloqadeのChain(3, lattice_spacing=6.0)に相当します。
いろんな配置
Pulserには便利な配置生成メソッドがあります。
from pulser import Register
# 正方格子
square_reg = Register.square(3, spacing=6.0) # 3x3
# 三角格子
triangle_reg = Register.triangular_lattice(3, 3, spacing=6.0)
# ハニカム格子
honeycomb_reg = Register.hexagonal_lattice(2, 2, spacing=6.0)
# それぞれ表示
square_reg.draw()
triangle_reg.draw()
honeycomb_reg.draw()
第3章: パルスシーケンスを組む
デバイスの選択
Pulserでは、まず使用する「デバイス」を選びます。デバイスによって、使えるパラメータの範囲が決まります。
from pulser import Sequence
from pulser.devices import AnalogDevice
# Registerを作成
coords = [(0, 0), (6, 0), (12, 0)]
reg = Register.from_coordinates(coords)
# シーケンスを作成
seq = Sequence(reg, AnalogDevice)
print(seq.available_channels)
AnalogDeviceは汎用的な設定で、シミュレーション用途に適しています。
チャンネルの宣言
次に、使用するチャンネルを宣言します。
# Rydbergグローバルチャンネルを宣言
seq.declare_channel("rydberg_global", "rydberg_global")
パルスの追加
from pulser import Pulse
from pulser.waveforms import RampWaveform, ConstantWaveform, CompositeWaveform
# パルスを定義(デバイスの最大振幅は約12.5 rad/μs)
rise = RampWaveform(100, 0, 10) # 100ns で 0→10 rad/μs
hold = ConstantWaveform(1000, 10) # 1000ns で 10 rad/μs を維持
fall = RampWaveform(100, 10, 0) # 100ns で 10→0 rad/μs
# パルスを結合(CompositeWaveformを使用)
pulse = Pulse(
amplitude=CompositeWaveform(rise, hold, fall),
detuning=ConstantWaveform(1200, 0), # デチューニングは0
phase=0
)
# シーケンスに追加
seq.add(pulse, "rydberg")
# 可視化
seq.draw()
実行すると、パルスシーケンスの波形が表示されます:

この辺りがBloqadeより物理寄りな設計です。Bloqadeでは.piecewise_linear()で簡潔に書けることを、Pulserでは明示的に波形を組み立てます。
第4章: シミュレーションを実行する
QuTiPバックエンド
PulserはQuTiP(Quantum Toolbox in Python)をバックエンドとして使います。
from pulser_simulation import QutipEmulator
# エミュレータを作成
sim = QutipEmulator.from_sequence(seq)
# シミュレーション実行
results = sim.run()
# 最終状態を取得
final_state = results.get_final_state()
print(final_state)
サンプリング(測定)
# 1000回サンプリング
counts = results.sample_final_state(N_samples=1000)
print(counts)
実行結果:
Simulation result (1000 samples):
101: 335
100: 191
001: 170
010: 150
000: 124
110: 15
011: 12
111: 3
-
0= 基底状態(|g⟩) -
1= リュードベリ状態(|r⟩)
101が最多なのは、両端の原子がリュードベリ状態になりやすく、真ん中は封鎖されているためです。
第5章: BloqadeとPulserを比較する
同じ問題を両方で解く
3原子の鎖でリュードベリ励起を行い、結果を比較してみましょう。
Bloqade版
from bloqade.analog.atom_arrangement import Chain
program = (
Chain(3, lattice_spacing=6.0)
.rydberg.rabi.amplitude.uniform
.piecewise_linear([0.1, 1.0, 0.1], [0, 15, 15, 0])
)
result = program.bloqade.python().run(shots=1000)
print(result.report().counts()[0])
Pulser版
from pulser import Register, Sequence, Pulse
from pulser.devices import AnalogDevice
from pulser.waveforms import RampWaveform, ConstantWaveform, CompositeWaveform
from pulser_simulation import QutipEmulator
# Register
coords = [(0, 0), (6, 0), (12, 0)]
reg = Register.from_coordinates(coords, prefix="q")
# Sequence
seq = Sequence(reg, AnalogDevice)
seq.declare_channel("rydberg", "rydberg_global")
# Pulse (時間はnsec単位、振幅は最大12.5 rad/μs)
rise = RampWaveform(100, 0, 10)
hold = ConstantWaveform(1000, 10)
fall = RampWaveform(100, 10, 0)
pulse = Pulse(
amplitude=CompositeWaveform(rise, hold, fall),
detuning=ConstantWaveform(1200, 0),
phase=0
)
seq.add(pulse, "rydberg")
# Simulation
sim = QutipEmulator.from_sequence(seq)
results = sim.run()
counts = results.sample_final_state(N_samples=1000)
print(counts)
コード量の比較
| 項目 | Bloqade | Pulser |
|---|---|---|
| 行数 | 約7行 | 約20行 |
| 抽象度 | 高い | 低い |
| 柔軟性 | 中程度 | 高い |
Bloqadeはメソッドチェーンでシンプルに書けます。
Pulserは明示的で、何をしているか分かりやすい。
どちらが良いかは好みと用途次第。
第6章: MIS問題を解く
Pulserでの実装
from pulser import Register, Sequence, Pulse
from pulser.devices import AnalogDevice
from pulser.waveforms import InterpolatedWaveform
from pulser_simulation import QutipEmulator
import numpy as np
# 3x3の正方格子
reg = Register.square(3, spacing=6.5, prefix="q")
reg.draw()
# シーケンス作成
seq = Sequence(reg, AnalogDevice)
seq.declare_channel("rydberg", "rydberg_global")
# 断熱的パルス(MIS用)
duration = 2600 # ns
t = np.linspace(0, duration, 100)
# Ω: 0 → 10 → 0(最大振幅は約12.5 rad/μs)
omega = 10 * np.sin(np.pi * t / duration) ** 2
# Δ: -10 → 10
delta = -10 + 20 * t / duration
pulse = Pulse(
amplitude=InterpolatedWaveform(duration, omega),
detuning=InterpolatedWaveform(duration, delta),
phase=0
)
seq.add(pulse, "rydberg")
seq.draw()
# シミュレーション
sim = QutipEmulator.from_sequence(seq)
results = sim.run()
counts = results.sample_final_state(N_samples=1000)
# 結果表示
sorted_counts = sorted(counts.items(), key=lambda x: x[1], reverse=True)
print("=== 出現頻度の高い状態 ===")
for state, count in sorted_counts[:5]:
print(f" {state}: {count}回 (励起数: {state.count('1')})")
実行結果:
=== 出現頻度の高い状態 ===
101000101: 269回 (励起数: 4)
101010101: 204回 (励起数: 5)
001100001: 46回 (励起数: 3)
010000101: 45回 (励起数: 3)
100000101: 37回 (励起数: 3)

101010101(チェッカーボードパターン)と101000101が上位に来ています。これらは隣り合う原子が同時に励起されていない、MIS問題の解候補です。
第7章: Pasqal Cloudへの接続(発展編)
Pasqalも実機へのクラウドアクセスを提供しています。
from pulser_pasqal import PasqalCloud
# Pasqal Cloudに接続(要アカウント)
connection = PasqalCloud(
username="your_email",
password="your_password",
project_id="your_project_id"
)
# ジョブ投入
job = connection.submit(seq, job_params={"runs": 100})
# 結果取得
result = job.result
おわりに
この記事で学んだこと
- PulserとBloqadeの違いと使い分け
- Pulserでの原子配置(Register)の定義
- パルスシーケンスの構築方法
- QuTiPエミュレータでのシミュレーション
- MIS問題の実装
- 両SDKの比較
どっちを選ぶ?
- 手軽に始めたい → Bloqade
- 物理を深く理解したい → Pulser
- AWS環境がある → Bloqade
- 研究で使いたい → 両方触ってみる
Discussion