メインコンテンツへスキップ

最初の量子実験

はじめに

以下の動画では、Olivia Lanes がこのレッスンの内容を説明しています。別ウィンドウで開きたい場合は、YouTube 動画をご利用ください。

ここまでで、最初の量子 Circuit を実行し、量子コンピューティングの基礎を学んできました。量子状態がどのように表現されるか、Gate がそれらの状態にどのように作用するか、そして重ね合わせやエンタングルメントといった量子的特性がどのように関わるかです。いよいよこれらすべてを実践に移して、量子コンピューターで最初の問題を解く時が来ました。

量子コンピューターが適した問題の広い全体像については、後のレッスンで探っていきます。今は、自然シミュレーションというドメインの問題に注目します。自然界の量子システムの代替として、よりクリーンで制御しやすいものとして量子コンピューターを使うというアプローチです。実際、これは 1980 年代にリチャード・ファインマンが量子コンピューターに最初に思い描いた応用でした。彼が有名な言葉を残しています。「自然は古典的ではないんだ!自然のシミュレーションをしたいなら、量子力学的に作らなければならない…」

このレッスンでは、その原則に従い、2 つのスピン(小さな磁石と考えてください)の相互作用をシミュレートします。相互作用の符号によって、スピンは同じ方向に揃おうとするか、逆方向を向こうとするかが決まります。後者のケースに注目します。より興味深く、より難しい挙動につながることが多いためです。この小さな 2 Qubit システムを理解したら、同じアイデアがどのようにスケールアップするかを示し、大規模なスピン系のシミュレーションで量子コンピューターがその指数的なスケーリングを活かせることを見ていきます。

2 つの相互作用する磁石

この問題では、モデル内の各スピンに 1 つずつ、合計 2 つの Qubit を使用します。各スピンは上向き(Qubit 状態 0|0\rangle)、下向き(Qubit 状態 1|1\rangle)、またはその 2 つの重ね合わせにある可能性があります。

スピンが反強磁性の相互作用を持つ場合、それらは反平行になろうとします。つまり、一方が上向きのとき、もう一方は下向きになりたがり、逆もまた然りです。

さらに、システム内に左右方向を向く磁場があるとします。この磁場は通常のスピンの上下方向を横切って向いているため、横磁場と呼ばれます。この磁場はスピンを反転させることができ、その結果、最低エネルギーの配置は特定の 1 つのスピンパターンではなく、上向きと下向きのスピン配置の特定の重ね合わせになります。

これらすべての効果は、ハミルトニアンと呼ばれる数学的対象で記述できます。ハミルトニアンは、与えられたスピンの配置に対するシステムのエネルギーを教えてくれます。

H=JZ1Z0+hx(X1+X0)H = J Z_1 Z_0 + h_x (X_1 + X_0)

ここで JJ はスピン間の相互作用の強さを制御する係数、hxh_x は外部磁場の強さの係数です。Z1Z0Z_1 Z_0 はスピンが揃っているか反平行かによって報酬またはペナルティを与え、X0X_0X1X_1 は磁場によるスピン反転効果を表します。

物理では、系は最も低いエネルギーの状態(基底状態と呼ばれる)に落ち着こうとします。この最低エネルギー状態を見つけることは一般的な問題ですが、このレッスンの範囲を超える最適化手法が必要です。

代わりに、もっとシンプルな問いを立てます。特定の状態にスピンを準備した場合、その状態のエネルギーはどれくらいか?

これに答えるために、以下を行います。

  1. 選択した状態にスピンを準備する
  2. 上のハミルトニアンを使ってその状態のエネルギーを測定する

これは、後のコースで学べる変分アルゴリズムなど、より大きな量子アルゴリズムの内部に登場するまさにこの種の計算です。

Qiskit の実装

コードを書き始める前に、少し文脈を整理する必要があります。量子 Circuit を実行するとき、常に Qubit の測定で終わります。しかし、その測定結果について 2 種類の問いを立てることができます。単に Qubit の状態を知りたいこともあれば、量子状態から物理量(エネルギーなど)の値を知りたいこともあります。

Qiskit では、この 2 種類の問いをプリミティブと呼ばれる 2 つの異なるツールで処理します。

Sampler は 1 番目の種類の問いに答えます。Circuit を何度も実行し、00011011 などの各可能な結果を何回測定したかを教えてくれます。結果は各測定結果の確率を示すヒストグラムです。

Estimator は 2 番目の種類の問いに答えます。ヒストグラムを提供する代わりに、多くの測定を裏側で組み合わせ、提供されたハミルトニアンに従って状態のエネルギーなど、単一の数値を計算します。

これらのツールをいつ、なぜ使うかを理解できるよう、同じ 2 Qubit システムに適用した 2 つの完全なワークフロー(「Qiskit パターン」と呼ばれる)を順を追って説明します。

Qiskit パターンのワークフロー

Qiskit パターンのワークフローは、Qiskit で量子問題を解くために使う一般的なフレームワークです。量子コンピューティングのタスクを 4 つのステップに分けます。

  1. 問題を量子 Circuit で表現できるモデルにマッピングする
  2. 特定の Backend で実行するよう Circuit を最適化する
  3. 選択した Backend で最適化された Circuit を実行する
  4. 生の測定データを後処理する

実験 1:Sampler を使って状態を測定する

マッピング

一般に、マッピングのステップは、実際の問題を Qubit、演算子、測定で表現する方法を考えるステップです。多くのアプリケーションでは、これがワークフローで最も難しく複雑な部分です。「各 Qubit は何を表すか?」といった単純な問いでさえ、常に明快な答えがあるとは限りません。

ただし、この実験では、マッピングは意図的にシンプルにしてあります。各物理的な自由度が 1 つの Qubit に直接対応します。この一対一の対応のため、マッピングのステップは、準備したい量子状態を選び、その状態を準備・測定する Circuit を書くことに縮約されます。

ここでは、このコースの最初のレッスンで作ったものと似たエンタングルしたベル状態を準備します。

Ψ=12(1001)\vert\Psi\rangle = \frac{1}{\sqrt{2}}(\vert 10\rangle - \vert 01\rangle)
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
# Import Qiskit primitives
from qiskit import QuantumCircuit

# Make state
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.x(1)
qc.z(0)

# Measure state
qc.measure_all()

# Draw circuit
qc.draw("mpl")

Output of the previous code cell

最適化

Circuit を量子コンピューター(またはその月の実際の量子コンピューターの無料時間を使い切った場合はシミュレーター)で実行する前に、実行の準備をする必要があります。このステップを最適化と呼びます。(注意:「最適化」という言葉の使い方は混乱を招くことがあります。量子コンピューティングでは、最適化問題は特定のクラスの問題を指します。ここでは最適化を、すべての量子 Circuit がハードウェア上で効率的に実行される前に経由する必須の準備ステップを表すために使っています。)

最適化の間に:

  1. Backend を選択します — 実際の量子コンピューターまたはシミュレーター。
  2. Circuit の Qubit をデバイス上の物理 Qubit に割り当てます。
  3. 量子コンピューターが実際に実行できる Gate のみを使って Circuit を書き直します。
  4. 必要に応じて、ノイズの影響を減らすためのエラー緩和・抑制手法を実装します。

Qiskit では、これはトランスパイラーによって自動的に処理されます。Backend を選べば、トランスパイラーが Circuit を実行準備完了にするすべての作業を行うため、Gate や Qubit の割り当てを手動で調整する必要はありません。トランスパイラーはさまざまな最適化レベルも提供しており、必要に応じてエラーを減らすのに役立ちます。最適化は「パス」と呼ばれるステージで行われます。したがって、以下のコードではこの最適化を pass_manager が処理します。エラーとエラー緩和についてさらに学ぶには、Olivia Lanes の実践的量子コンピューティングコースをご覧ください。

# Load the Qiskit Runtime service
from qiskit_ibm_runtime import QiskitRuntimeService

## Load the Qiskit Runtime service
# QiskitRuntimeService.save_account(
# channel="ibm_quantum_platform",
# token="YOUR_TOKEN_HERE",
# overwrite=True,
# set_as_default=True,
# )
# service = QiskitRuntimeService(channel="ibm_quantum_platform")

# Or load saved credentials
service = QiskitRuntimeService()

# Use the least busy backend, or uncomment the loading of a specific backend like "ibm_brisbane".
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
# backend = service.backend("ibm_brisbane")
print(backend.name)
ibm_fez
# Transpile the circuit and optimize for running on the quantum computer selected
# Step 2: Transpile
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)

qc_isa.draw("mpl")

Output of the previous code cell

実行

実行の準備ができました!Sampler を読み込み、ジョブを Backend に送信します。

# Load the Runtime primitive and session
from qiskit_ibm_runtime import SamplerV2 as Sampler

sampler = Sampler(mode=backend)

シミュレーターを使用する場合は、代わりにこのセルのコメントを外して実行できます。

## Load the backend sampler
# from qiskit.primitives import BackendSamplerV2

## Load the Aer simulator and generate a noise model based on the currently-selected backend.
# from qiskit_aer import AerSimulator
# from qiskit_aer.noise import NoiseModel

# noise_model = NoiseModel.from_backend(backend)

## Define a simulator using Aer, and use it in Sampler.
# backend_sim = AerSimulator(noise_model=noise_model)
# sampler_sim = BackendSamplerV2(backend=backend_sim)

## Alternatively, load a fake backend with generic properties and define a simulator.
## backend_gen = GenericBackendV2(num_qubits=18)
## sampler_gen = BackendSamplerV2(backend=backend_gen)
job = sampler.run([qc_isa], shots=100)
# job = sampler_sim.run([qc_isa]) # uncomment if you want to run on a simulator
res = job.result()
counts = res[0].data.meas.get_counts()

後処理

from qiskit.visualization import plot_histogram

print("counts = ", counts)
plot_histogram(counts)
counts =  {'10': 49, '01': 50, '11': 1}

Output of the previous code cell

カウントの大部分が 01 または 10 にあることがわかります。つまり、一方の Qubit が 0 と測定されたとき、もう一方は 1 であり、逆もまた然りです。これは準備したベル状態 Ψ\vert \Psi^- \rangle と一致しています。

実験 2:Estimator を使ってエネルギーを測定する

量子状態をサンプリングする方法を見たので、次は Estimator を使ってベル状態 Ψ=12(0110)\vert \Psi^- \rangle = \frac{1}{\sqrt{2}}(\vert 01 \rangle - \vert 10 \rangle) のエネルギーを計算しましょう。

マッピング

復習すると、系のエネルギーはハミルトニアンが捉えるスピン間の相互作用(JJ)と外部磁場(hxh_x)によって決まります。

H=JZ1Z0+hx(X1+X0)H = J Z_1 Z_0 + h_x (X_1 + X_0)

ハミルトニアンの各項は、スピンの特定の組み合わせがエネルギーにどのように寄与するかを教えてくれます。Qiskit では、これらの項をパウリ演算子として表現できます。パウリ演算子は Qubit への単純な作用を表すラベルです。

  • Z1Z0Z_1 Z_0 は両方の Qubit に ZZ を作用させます。
  • X0X_0 は Qubit 0 に XX を作用させます。
  • X1X_1 は Qubit 1 に XX を作用させます。

Qiskit の SparsePauliOp は、これらのパウリ演算子をその数値係数とともに格納する方法です。これらのパウリ演算子が、量子コンピューターに測定させたいオブザーバブル(系に関する情報を教えてくれる量)です。Estimator を使って、各オブザーバブルの状態に対する平均値を計算し、ハミルトニアンの係数に従って組み合わせることで、総エネルギーを得ることができます。

# Import Qiskit primitives
from qiskit.quantum_info import SparsePauliOp

# Parameters
J = 1.0 # antiferromagnetic coupling (J<0)
hx = -0.5 # transverse field strength

# 1. Define the Hamiltonian H = J Z1 Z2 + hx (X1 + X2)
obs = SparsePauliOp.from_list([("ZZ", J), ("XI", hx), ("IX", hx)])

# Make state
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.x(1)
qc.z(0)
<qiskit.circuit.instructionset.InstructionSet at 0x1387ed630>

コードで qc.measure_all() の行を省略したことに注意してください。Estimator では、Circuit のどこで測定するかを指定する必要がないためです。推定したいオブザーバブルを伝えるだけで、Qiskit が裏側で測定を処理してくれます。

最適化

最適化のステップは前と同様ですが、オブザーバブルも量子コンピューターが理解できる形式で記述されていることを確認する点が追加されています。

# Transpile the circuit and optimize for running on the quantum computer selected
# Step 2: Transpile
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)
obs_isa = obs.apply_layout(layout=qc_isa.layout)

qc_isa.draw("mpl")

Output of the previous code cell

実行

実行のステップでは、Estimator を読み込み、Circuit と推定させたいオブザーバブルのリストを量子コンピューターに送信します。

# Load the Runtime primitive and session
from qiskit_ibm_runtime import EstimatorV2 as Estimator

estimator = Estimator(mode=backend)
# Load the backend sampler

# noise_model = NoiseModel.from_backend(backend)

# Use Aer simulator in Estimator
# estimator_sim = BackendEstimatorV2(backend=backend_sim)

# Alternatively, load a fake backend with generic properties and define a simulator.
# backend_gen = GenericBackendV2(num_qubits=18)
# estimator_gen = BackendEstimatorV2(backend=backend_gen)
pubs = [(qc_isa, obs_isa)]
job = estimator.run([[qc_isa, obs_isa]])
res = job.result()

# Uncomment lines below to run the job on the Aer simulator with noise model from real backend
# job = estimator_sim.run([[qc_isa,obs_isa]])
# res=job.result()

後処理

最後に、後処理のステップでは、Estimator が裏側で計算したエネルギーを出力するだけです。

print(res[0].data.evs)
-0.9934112021453058

これが私たちの状態のエネルギーです!

まとめ

このレッスンでは、2 つの相互作用するスピンを表すシンプルな 2 Qubit 量子状態を準備する方法を学びました。Sampler を使って測定結果の分布を観察する方法と、Estimator を使ってハミルトニアンに従って状態のエネルギーを計算する方法を見ました。その過程で、ハミルトニアンがスピン間の相互作用と外部磁場の効果をどのようにエンコードするか、そして異なる状態が異なるエネルギーを持つことができることを確認しました。

多数のスピンへの拡張

これまで 2 つのスピンのみを見てきました。手計算で分析できるほど単純です。実際の物理系(磁石や他の複雑な物質など)では、多くの相互作用するスピンが存在することが多いです。スピンの数が増えると、ハミルトニアンはより複雑になり、最低エネルギー状態を見つけることははるかに難しくなります。ここで量子コンピューターが役立ちます。異なる状態を準備してエネルギーを推定することで、大規模なシステムに対して古典コンピューターよりも効率的に低エネルギーの配置を探索できます。

この実験の自然な拡張は、より多くのスピンを表すために Qubit の数を増やし、最低エネルギー状態を見つけようとするスピンの準備方法を調整することです。このアプローチは変分法の本質であり、変分量子アルゴリズムコースで学ぶことができます。

基底状態エネルギーを研究するための他の量子的アプローチも存在します。変分法を超えるこれらの方法はここでは取り上げませんが、興味があれば量子対角化アルゴリズムコースで紹介されています。

学習目標

実験 2 の最初に戻って、異なる重ね合わせ状態で試してみてください。使った状態よりも低いエネルギーの状態を見つけられますか?

This translation based on the English version of 2026年5月7日