Parameterを利用したBuilder構文

# はじめに
Builder構文はParameterクラスと併用することで威力を発揮する構文です。この記事ではParameterクラスを使ったBuilder構文の書き方を紹介したいと思います。

まず、Qiskit Pulseを動かすために必要な記述をまとめておきます。

```python
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クラスは変数パラメーターのためのクラスとなります。

https://qiskit.org/documentation/stubs/qiskit.circuit.Parameter.html

これだけだとよくわからないのでソースコードを記述してみます。
試しにQiskit Textbookにある、周波数の校正のソースコードを例にして紹介したいと思います。


```python
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)
```
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78339/3f195b63-a727-51c5-7493-64d4b38ea9c2.png)

1. 周波数領域の決定  
sweepする周波数を設定します。ここでは中心となる周波数の値を`backend_defaults`から取得しています。
1. パルスに必要な値の設定  
パルスに必要な値の設定を行います。
1. Parameterクラスの定義  
Parameterクラスを定義します。ここでは変数となる値`freq`をクラスに渡しています。
1. Builder構文  
Builder構文を利用してScheduleを作成します。
1. Parameterクラスで定義した変数を周波数に設定  
`set_frequency()`を利用してScheduleで利用する周波数を定義します。ここでは実際の値を定義するのではなくParameterクラスを定義しておきます。
1. 周波数領域とParameterクラスを使って定義したScheduleをバインド  
ここではじめて実際の周波数の値とScheduleを紐づけることになります。


今回の例以外にもParameterクラスに振幅などを変数にしてsweepさせることもできますし、Parameterクラスを複数使用することもできます。

```python
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」を以下のように再利用できます。

```python
# 周波数領域と振幅(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]
```

```python
# 周波数領域と振幅(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を使いまわすことが可能で再利用性が高くなっています。