はじめに
Qiskitは量子プログラミング用SDKのパイオニアであり、全世界から無料の量子コンピュータに接続できる機能を有することから、多くのユーザーに愛されています。
Qiitaでも、Qiskitで書かれた多くの量子プログラムや、実機実行結果を目にすることができます。
Qiskitは、量子コンピュータの大衆化を成功させたと思っています。
しかし、2024/12時点で、Qiskit が0.x から 1.x に major updateされ、破壊的な変更が続き、Webにあるコードの多くが実行不可能となっています。
加えて、IBM自身が公開するサイト(IBM Quantum Documentation)も更新が追いついておらず、現在では実行不可能なものが掲載されています。
特に日本語では、正常動作するexampleに到達することがもはや困難であり、多くの方が困っているのではないかと危惧しています。
そこで、チートシートを作成し、公開することに決めました。
Qiskit 0.x を使っていた方であれば、移行の助けになるものと思います。
本記事は随時アップデートしていきます。
動作確認環境
2024/12
Name: qiskit
Version: 1.3.0
Name: qiskit-ibm-runtime
Version: 0.33.2
Name: qiskit-aer
Version: 0.15.1
教材
APIリファレンス
-
Qiskit SDK
Runtime等のAPIリファレンスも、ページ上部のタブから移動できます。
実機マイページ
環境構築
pip install qiskit
pip install qiskit-ibm-runtime
pip install qiskit-aer
pip install pylatexenc
pip install matplotlib
pylatexencは、回路描画する場合に必要ですので、インストール推奨です。
IBMQに登録→ログイン(二段階認証が必要です)→ token文字列をコピーしておきます。
以下の"hoge"をtoken文字列に差し替えてください。
from qiskit_ibm_runtime import QiskitRuntimeService
token = "hogehoge"
QiskitRuntimeService.save_account(
token=token,
channel="ibm_quantum" # `channel` distinguishes between different account types
)
アカウント情報がセーブされていますので、以降はロードするだけでよくなります。
# Load saved credentials
service = QiskitRuntimeService()
回路の記述
import numpy as np
from qiskit import QuantumCircuit
qc = QuantumCircuit(2)
qc.x(0)
qc.h([0,1])
qc.rx(theta=np.pi/4,qubit=0)
qc.cx(0,1) # CNOT
qc.measure_all() # Measure all qubits
qc.draw("mpl")
状態ベクトルの出力
qc.remove_final_measurements() # no measurements allowed
from qiskit.quantum_info import Statevector
statevector = Statevector(qc) # qc is a quantum circuit
print(statevector)
Statevector([ 0.46193977+0.19134172j, -0.46193977-0.19134172j,
0.46193977+0.19134172j, -0.46193977-0.19134172j],
dims=(2, 2))
ケット表示出力(Latex)
statevector.draw("latex") # psi is a Statevector object
ケット表示の文字列へ変換する例
def dec_to_bin(decimal_number:int = 10, bit_len:int = None):
binary_string = bin(decimal_number)[2:].zfill(bit_len)
return binary_string
def sv_to_ket_str(sv_array):
n_qubit = int(np.log2(len(sv_array)))
ket = ""
for idx, val in enumerate(sv_array):
# ignore small value
if np.abs(np.real(val)) < 1e-10:
val = 0.0 + 1j*np.imag(val)
if np.abs(np.imag(val)) < 1e-10:
val = np.real(val) + 0.0*1j
ket += str(val) + "|" + dec_to_bin(idx, n_qubit) + ">" + " + "
ket = ket[:-2]
return ket
ket = sv_to_ket_str(statevector)
print(ket)
(0.46193976625564326+0.19134171618254484j)|00> + (-0.46193976625564326-0.19134171618254484j)|01> + (0.46193976625564326+0.19134171618254484j)|10> + (-0.46193976625564326-0.19134171618254484j)|11>
回路の実行(バックエンドの指定)
バックエンドは、大きく3種類に分かれます。
- Qiskit SDK Primitives
- ローカルPCでのシミュレーション。雑音を考慮できない。
- Qiskit Aer Primitives
- ローカルPCでのシミュレーション。雑音を考慮できる。アルゴリズムが豊富。
- スタビライザ追跡アルゴリズムでのシミュレーション等はこちら。
- Qiskit RUntime Primitive
- 実機実行、または互換性のあるコードでのローカルPCシミュレーション(雑音考慮)。
Exact simulation with Qiskit SDK primitives
The reference primitives in the Qiskit SDK perform local statevector simulations.
Exact and noisy simulation with Qiskit Aer primitives
High-performance quantum computing simulators with realistic noise models.
In this article, we demonstrate the use of Qiskit Aer primitives for exact and noisy simulation.
Qiskit SDK Primitives
Observable 期待値
期待値計算は Estimator という種類のシミュレーターを使います。
from qiskit import QuantumCircuit
# circuit for which you want to obtain the expected value
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0,1)
qc.draw("mpl", style="iqp")
from qiskit.quantum_info import SparsePauliOp
import numpy as np
# observable(s) whose expected values you want to compute
from qiskit.quantum_info import SparsePauliOp
observable = SparsePauliOp(["II", "XX", "YY", "ZZ"], coeffs=[1, 1, -1, 1]) # II+XX-YY+ZZ
from qiskit.primitives import StatevectorEstimator
estimator = StatevectorEstimator()
job = estimator.run([(qc, observable,)])
result = job.result()
print(f" > Expectation value: {result[0].data.evs}")
Expectation value: 3.999999999999999
回路に埋め込んだ変数を変えながら実行。
かつ、実機を意識してレイアウトし直し(トランスパイル)。
from qiskit import QuantumCircuit
from qiskit.circuit import Parameter
# circuit for which you want to obtain the expected value
qc = QuantumCircuit(2)
qc.ry(Parameter('theta'), 0)
qc.h(0)
qc.cx(0,1)
qc.draw("mpl", style="iqp")
from qiskit.quantum_info import SparsePauliOp
import numpy as np
# observable(s) whose expected values you want to compute
from qiskit.quantum_info import SparsePauliOp
observable = SparsePauliOp(["II", "XX", "YY", "ZZ"], coeffs=[1, 1, -1, 1])
# value(s) for the circuit parameter(s)
parameter_values = [[0], [np.pi/6], [np.pi/2]]
# Generate a pass manager without providing a backend
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit = pm.run(qc)
isa_observable = observable.apply_layout(isa_circuit.layout)
from qiskit.primitives import StatevectorEstimator
estimator = StatevectorEstimator()
job = estimator.run([(isa_circuit, isa_observable, parameter_values)])
result = job.result()
print(f" > Expectation value: {result[0].data.evs}")
Expectation value: [4. 3.73205081 2. ]
Sampling
状態ベクトルから確率分布を計算し、疑似乱数でサンプルを生成します。
サンプル(あるいは確率分布)を取るのは Sampler という種類のシミュレーターを使います。
By default, the reference Sampler performs an exact statevector calculation based on the quantum_info.Statevector class.
# Define quantum circuit with 2 qubits
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
circuit.draw()
# Transpile circuit
pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit = pm.run(circuit)
# Run using sampler
from qiskit.primitives import StatevectorSampler
sampler = StatevectorSampler()
result = sampler.run([circuit]).result()
# Access result data for PUB 0
data_pub = result[0].data
# Access bitstring for the classical register "meas"
bitstrings = data_pub.meas.get_bitstrings()
print(f"The number of bitstrings is: {len(bitstrings)}")
# Get counts for the classical register "meas"
counts = data_pub.meas.get_counts()
print(f"The counts are: {counts}")
The number of bitstrings is: 1024
The counts are: {'00': 509, '11': 515}
なお、シミュレーションなのでトランスパイルを飛ばしても動きます。
こんな感じ
from qiskit.primitives import StatevectorSampler as Sampler
# initialization of the sampler
sampler = Sampler()
# collect 128 shots from the quantum circuit
job = sampler.run([qc], shots=128)
result = job.result()
# Get results for the first (and only) PUB
pub_result = result[0]
print(f" > Counts: {pub_result.data.meas.get_counts()}")
Counts: {'01': 28, '00': 29, '11': 33, '10': 38}
from qiskit.visualization import plot_histogram
plot_histogram(pub_result.data.meas.get_counts())
Parall execution
job = sampler.run([qc,qc,qc], shots=128)
result = job.result()
for i in range(3):
pub_result = result[i]
print(f" > Counts: {pub_result.data.meas.get_counts()}")
Counts: {'01': 29, '10': 40, '11': 28, '00': 31}
Counts: {'01': 43, '10': 23, '00': 28, '11': 34}
Counts: {'01': 29, '11': 32, '10': 36, '00': 31}
Qiskit Aer primitives
In this article, we demonstrate the use of Qiskit Aer primitives for exact and noisy simulation.
雑音なしシミュレーション
Simulating your first quantum program with Qiskit Aer
一見、qiskit SDK primitivesと同じですが、qiskit_aerからimportをしています。
import qiskit
from qiskit_aer.primitives import SamplerV2
# Generate 3-qubit GHZ state
circ = qiskit.QuantumCircuit(3)
circ.h(0)
circ.cx(0, 1)
circ.cx(1, 2)
circ.measure_all()
# Construct an ideal simulator with SamplerV2
sampler = SamplerV2()
job = sampler.run([circ], shots=128)
# Perform an ideal simulation
result_ideal = job.result()
counts_ideal = result_ideal[0].data.meas.get_counts()
print('Counts(ideal):', counts_ideal)
Counts(ideal): {'111': 61, '000': 67}
雑音ありシミュレーション
Device backend noise model simulations
from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator
from qiskit.visualization import plot_histogram
雑音なしの場合
# Construct quantum circuit
circ = QuantumCircuit(3, 3)
circ.h(0)
circ.cx(0, 1)
circ.cx(1, 2)
circ.measure([0, 1, 2], [0, 1, 2])
sim_ideal = AerSimulator()
# Execute and get counts
result = sim_ideal.run(transpile(circ, sim_ideal)).result()
counts = result.get_counts(0)
plot_histogram(counts, title='Ideal counts for 3-qubit GHZ state')
雑音あり(ibm_quebec模擬)の場合
quebecの雑音モデルを取得するために、qiskit_ibm_runtimeを使う(依存性あり)。
from qiskit_ibm_runtime.fake_provider import FakeQuebec
device_backend = FakeQuebec()
sim_quebec = AerSimulator.from_backend(device_backend)
# Transpile the circuit for the noisy basis gates
tcirc = transpile(circ, sim_quebec)
# Execute noisy simulation and get counts
result_noise = sim_quebec.run(tcirc).result()
counts_noise = result_noise.get_counts(0)
plot_histogram(counts_noise,
title="Counts for 3-qubit GHZ state with device noise model")
Qiskit Runtime Primitives
Local testing mode
Local testing mode
大きく2種類がある。
- Fake backend + Qiskit Runtime Primitives
- Fake backend + Aer simulator (前述)
Aer simを使う場合、スタビライザ追跡アルゴリズムでのシミュレーション等もできる。
(特定構造や特定用途でシミュレーション時間に大きな差が出る。)
残る、Fake backend + Qiskit Runtime Primitives の場合だけ示します。
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import SamplerV2 as Sampler
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import SamplerV2 as Sampler
from qiskit_ibm_runtime.fake_provider import FakeQuebec
# Bell Circuit
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
fake_quebec = FakeQuebec()
pm = generate_preset_pass_manager(backend=fake_quebec, optimization_level=1)
isa_qc = pm.run(qc)
# You can use a fixed seed to get fixed results.
options = {"simulator": {"seed_simulator": 42}}
sampler = Sampler(mode=fake_quebec, options=options)
result = sampler.run([isa_qc]).result()
counts_noise = result[0].data.meas.get_counts()
plot_histogram(counts_noise,
title="Counts for a state with device noise model")
実機実行
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.quantum_info import SparsePauliOp, random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2 as Estimator
n_qubits = 127
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=n_qubits)
mat = np.real(random_hermitian(n_qubits, seed=1234))
circuit = IQP(mat)
observable = SparsePauliOp("Z" * n_qubits)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
estimator = Estimator(backend)
job = estimator.run([(isa_circuit, isa_observable)])
result = job.result()
print(f" > Expectation value: {result[0].data.evs}")
print(f" > Metadata: {result[0].metadata}")
Expectation value: 0.00732421875
Metadata: {'shots': 4096, 'target_precision': 0.015625, 'circuit_metadata': {}, 'resilience': {}, 'num_randomizations': 32}
Qiskit ecosystem (関連ライブラリ)
おわりに
本記事は随時アップデートしていきます。
Qiskitは量子プログラミング用SDKのパイオニアであり、日本においてもこの発展に追随・貢献できる環境を維持・整備しておくことに、意味があると思います。
量子コンピュータの実現には長い長い時間がかかりますから、これから参入する方へのハードルを上げてはいけません。
この思いに賛同していただける方は、ぜひ「いいね」を押して頂けると嬉しいです。