はじめに
Builder構文はParameterクラスと併用することで威力を発揮する構文です。この記事ではParameterクラスを使ったBuilder構文の書き方を紹介したいと思います。
まず、Qiskit Pulseを動かすために必要な記述をまとめておきます。
import numpy as np
from qiskit import QuantumCircuit, transpile, Aer, IBMQ
from qiskit.visualization import *
from qiskit.circuit import Gate
from qiskit.circuit import Parameter
from qiskit import pulse
IBMQ.load_account()
provider = IBMQ.get_provider(hub='ibm-q', group='open', project='main')
backend = provider.get_backend('ibmq_armonk')
backend_defaults = backend.defaults()
backend_config = backend.configuration()
Parameterクラスを使ったBuilder構文
Parameterクラスは変数パラメーターのためのクラスとなります。
これだけだとよくわからないのでソースコードを記述してみます。
試しにQiskit Textbookにある、周波数の校正のソースコードを例にして紹介したいと思います。
import numpy as np
GHz = 1.0e9 # Gigahertz
MHz = 1.0e6 # Megahertz
# サンプル数は16の倍数である必要があります
def get_closest_multiple_of_16(num):
return int(num + 8 ) - (int(num + 8 ) % 16)
qubit=0
mem_slot=0
# 1 周波数領域の決定
center_frequency_Hz = backend_defaults.qubit_freq_est[qubit]
frequency_span_Hz = 40 * MHz
frequency_step_Hz = 1 * MHz
frequency_min = center_frequency_Hz - frequency_span_Hz / 2
frequency_max = center_frequency_Hz + frequency_span_Hz / 2
frequencies_GHz = np.arange(frequency_min / GHz,
frequency_max / GHz,
frequency_step_Hz / GHz)
# 2 パルスに必要な値の設定
drive_sigma_sec = 0.075e-6
drive_duration_sec = drive_sigma_sec * 8
drive_amp = 0.05
# 3 Parameterクラスの定義
freq = Parameter('freq')
# 4 Builder構文
with pulse.build(backend=backend, default_alignment='sequential') as parametrized_sched:
# 5 Parameterクラスで定義した変数を周波数に設定
pulse.set_frequency(freq, pulse.drive_channel(qubit))
pulse.play(pulse.Gaussian(duration=get_closest_multiple_of_16(pulse.seconds_to_samples(drive_duration_sec)),
amp=drive_amp,
sigma=pulse.seconds_to_samples(drive_sigma_sec),
name='spect_pulse'), pulse.drive_channel(qubit))
pulse.measure(qubits=[qubit], registers=[pulse.MemorySlot(mem_slot)])
# 6 周波数領域とParameterクラスを使って定義したScheduleをバインド
frequencies_Hz = frequencies_GHz*GHz
schedules = [parametrized_sched.assign_parameters({freq: f}, inplace=False) for f in frequencies_Hz]
schedules[0].draw(backend=backend)
- 周波数領域の決定
sweepする周波数を設定します。ここでは中心となる周波数の値をbackend_defaults
から取得しています。 - パルスに必要な値の設定
パルスに必要な値の設定を行います。 - Parameterクラスの定義
Parameterクラスを定義します。ここでは変数となる値freq
をクラスに渡しています。 - Builder構文
Builder構文を利用してScheduleを作成します。 - Parameterクラスで定義した変数を周波数に設定
set_frequency()
を利用してScheduleで利用する周波数を定義します。ここでは実際の値を定義するのではなくParameterクラスを定義しておきます。 - 周波数領域とParameterクラスを使って定義したScheduleをバインド
ここではじめて実際の周波数の値とScheduleを紐づけることになります。
今回の例以外にもParameterクラスに振幅などを変数にしてsweepさせることもできますし、Parameterクラスを複数使用することもできます。
freq = Parameter('freq')
amp = Parameter('amp')
with pulse.build(backend=backend, default_alignment='sequential') as parametrized_sched2:
pulse.set_frequency(freq, pulse.drive_channel(qubit))
pulse.play(pulse.Gaussian(duration=get_closest_multiple_of_16(pulse.seconds_to_samples(drive_duration_sec)),
amp=amp,
sigma=pulse.seconds_to_samples(drive_sigma_sec),
name='spect_pulse'), pulse.drive_channel(qubit))
pulse.measure(qubits=[qubit], registers=[pulse.MemorySlot(mem_slot)])
何が便利かというと、ここで定義したScheduleを似た実験で使いまわすことができます。例えば低エネルギーと高エネルギーで周波数をスイープしたい場合、上記のSchedule「parametrized_sched2」を以下のように再利用できます。
# 周波数領域と振幅(low power)をParameterクラスを使って定義したScheduleとバインド
frequencies_Hz = frequencies_GHz*GHz
schedules = [parametrized_sched2.assign_parameters({freq: f, amp: 0.1}, inplace=False) for f in frequencies_Hz]
# 周波数領域と振幅(high power)をParameterクラスを使って定義したScheduleとバインド
frequencies_Hz = frequencies_GHz*GHz
schedules = [parametrized_sched2.assign_parameters({freq: f, amp: 1.0}, inplace=False) for f in frequencies_Hz]
#まとめ
この記事ではParameterを利用したBuilder構文について紹介しました。Parameterクラスを利用することでScheduleをfor文の中に入れて制御する必要がなくなりソースコードをすっきりさせることができます。また変数を制御フロー(Schedule)と別に定義できることから、Scheduleを使いまわすことが可能で再利用性が高くなっています。