初めてQiskitを使ってからQiskitで量子機械学習ができるようになるまで 〜パート1〜

# Qiskitチュートリアル
こんにちばんわ、東京理科大学大学院で修士課程の院生をしています、秋田と言います(以後お見知りおきを)。
今回はQiskitについての解説をしていくので、ポップコーンでも食べながらゆっくりしていってください！
Qiskitとは、IBM社の量子コンピューティング用Pythonライブラリのことで、これを用いて量子コンピュータの(現在の)挙動を確認してみましょう。

## Qiskitの実行環境の準備
本記事ではGoogleのサービス"Colaboratory"、通称"Colab"で実行を行います。まずはColabでもQiskitが使えるように実行環境を整えましょう。

```python
!pip install qiskit
!pip install qiskit[visualization]
```

次に、IBM Quantumのアカウントにアクセスします。まだアカウントを作っていない方は是非この機会に！
![スクリーンショット 2022-05-03 16.41.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653215/295786c3-dc49-7651-a6d4-6aa1413643e9.png)
以下のコードの

```
save_account('')
```

の('')の中に自身のAPIトークンを添付します。

```python
from qiskit import IBMQ
IBMQ.save_account('*****')
```

ここで一応Qiskitのバージョンの確認をしておきましょう。

```python
import qiskit
qiskit.__qiskit_version__
```

自分のところは

{'qiskit-terra': '0.20.1', 'qiskit-aer': '0.10.4', 'qiskit-ignis': '0.7.0', 'qiskit-ibmq-provider': '0.19.1', 'qiskit-aqua': None, 'qiskit': None, 'qiskit-nature': None, 'qiskit-finance': None, 'qiskit-optimization': None, 'qiskit-machine-learning': None}

と出てきました。
今度は、必要(そう)なモジュールをとりあえずインポートしましょう。

```python
import numpy as np
from numpy import pi
import math
import matplotlib.pyplot as plt
from qiskit import QuantumCircuit, execute, Aer, IBMQ, ClassicalRegister, QuantumRegister
from qiskit.circuit import Parameter
from qiskit.qasm import pi
from qiskit.tools.visualization import plot_histogram, circuit_drawer
```

また、せっかくIBM Quantumのアカウントにアクセスしたので、Providerの確認をしておきたいところです。これは現在どのマシンが稼働できるかを毎回確認し、誤ったマシンを設定したまま動かしてエラーが起きるというのを防ぎたいためでもあります。

```python
provider = IBMQ.load_account()
provider.backends()
```

[IBMQSimulator('ibmq_qasm_simulator') from IBMQ(hub='ibm-q', group='open', project='main'),
 IBMQBackend('ibmq_armonk') from IBMQ(hub='ibm-q', group='open', project='main'),
 IBMQBackend('ibmq_santiago') from IBMQ(hub='ibm-q', group='open', project='main'),
 IBMQBackend('ibmq_bogota') from IBMQ(hub='ibm-q', group='open', project='main'),
 IBMQBackend('ibmq_lima') from IBMQ(hub='ibm-q', group='open', project='main'),
 IBMQBackend('ibmq_belem') from IBMQ(hub='ibm-q', group='open', project='main'),
 IBMQBackend('ibmq_quito') from IBMQ(hub='ibm-q', group='open', project='main'),
 IBMQSimulator('simulator_statevector') from IBMQ(hub='ibm-q', group='open', project='main'),
 IBMQSimulator('simulator_mps') from IBMQ(hub='ibm-q', group='open', project='main'),
 IBMQSimulator('simulator_extended_stabilizer') from IBMQ(hub='ibm-q', group='open', project='main'),
 IBMQSimulator('simulator_stabilizer') from IBMQ(hub='ibm-q', group='open', project='main'),
 IBMQBackend('ibmq_manila') from IBMQ(hub='ibm-q', group='open', project='main')]

と出てきました(多少手直ししてます)。
ついでに今どのマシンが一番空いているかを確認することもできます。

```python
from qiskit.providers.ibmq import least_busy
backend_lb = least_busy(provider.backends(simulator=False, operational=True))
print("Least busy backend: ", backend_lb)
```

ここまではできれば毎回しておきましょう。

## 量子回路の実装
さて、これからいよいよ量子回路を作っていきます。まずは簡単な回路から作ってみましょう。

```python
qc = QuantumCircuit(2)

qc.h(0)
qc.cx(0, 1)

qc.draw('mpl')
```

するとこのような回路図ができたのではないでしょうか？
![回路図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653215/f9ba97d2-d74e-bfea-1649-36f889c15bce.png)
これは、$2$量子ビット$q_0$, $q_1$のみの回路図を考えており(QuantumCicuit()の()の数は量子ビットの数)、基本的なこととして、$1$量子ビットゲートはターゲットとなる量子ビットのインデックスを引数にとり、回転ゲートなどはそこに回転角を引数にとります。また、$2$量子ビットゲートは、制御量子ビット、ターゲット量子ビットの順にインデックスを引数にとります。
```
qc.draw('mpl')
```
これは量子回路図の描画をしています。('mpl')は見た目のオプション(matplotlib)です。

さて、この回路図ですが、まず$q_0$に$H$ゲートを置いたので測定確率が、"$00$"が50%、"$01$"が50%となります。その横に$CNOT$ゲートを置いたので$q_0$が"$0$"のとき$q_1$は"$0$"、$q_0$が"$1$"のとき$q_1$は"$1$"となっています(いわゆるBell状態です)。

続いて、量子回路を実行してみましょう。Qiskitは量子回路の実行において様々なシミュレータや実機をバックエンドとして提供しており、以下のものがその代表例です。

1.   'statevector_simulator'
2.   'unitary_simulator'
3.   'qasm_simulator'
4.   各量子ハードウェア

### statevector_simulator
理想的な量子回路における出力状態ベクトルを計算します。

```python
backend = Aer.get_backend('statevector_simulator')
results = execute(qc, backend=backend).result()
state_vec = results.get_statevector(qc)

print(state_vec)
```

次のように出てきましたね。

Statevector([0.70710678+0.j, 0.        +0.j, 0.        +0.j, 0.70710678+0.j], dims=(2, 2))

```
state_vec = results.get_statevector(qc)
```

ここの引数にqcを取っていますが、扱う量子回路の情報は既にresultsの中に入っているのでなくても良いです。

Aerはシミュレータのバックエンドを管理しており、executeで実行できます。今は'statevector_simulator'をバックエンドとして使用するのでAer.get_backend()の引数に'statevector_simulator'をとります。executeの引数には、実行する回路qcとバックエンドをとり、result()メソッドで結果を取得した後、resultsに保存します。resultsの中のstatevectorの情報だけを出力したいのでstate_vecに保存します。

実行結果から、$2$量子ビットからなるので$2^2$個の要素を持つ状態ベクトルが出力されていることが確認できます。これは、$\frac{1}{\sqrt 2}(|00\rangle+|11\rangle)$が正しく得られているということです。

### unitary_simulator
理想的な量子回路と等価なユニタリ変換行列を計算します。

```python
backend = Aer.get_backend('unitary_simulator')
results = execute(qc, backend=backend).result()
unitary_mat = results.get_unitary()

print(np.round(unitary_mat, 4))
```

実行すると

[[ 0.7071+0.j  0.7071-0.j  0.    +0.j  0.    +0.j]
 [ 0.    +0.j  0.    +0.j  0.7071+0.j -0.7071+0.j]
 [ 0.    +0.j  0.    +0.j  0.7071+0.j  0.7071-0.j]
 [ 0.7071+0.j -0.7071+0.j  0.    +0.j  0.    +0.j]]

となり、上で行われた量子回路全体のユニタリ変換行列が出てきたと思います。

### qasm_simulator
複数回の測定・サンプリングを伴う量子回路の実行ができます。

今までのバックエンドでは量子状態の計算結果を直接確認するものだったのですが、実際には必ず"測定"により確率的に出力された結果を得ることになります。

```python
qc_qasm = QuantumCircuit(2, 2)

qc_qasm.h(0)
qc_qasm.cx(0, 1)

qc_qasm.measure(0, 0)
qc_qasm.measure(1, 1)

qc_qasm.draw('mpl')
```
![回路図2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653215/3628efa9-638b-9a30-3c08-1d325adc259d.png)
ここで、

```
qc_qasm = QuantumCircuit(2, 2)
```

このように新しくqc_qasmという回路を作り直しました。QuantumCircuit()の第2引数には、測定結果を保存する古典的レジスタのサイズを入れています。多くの場合は量子レジスタと同数です。

測定ゲートの引数は次のようにします。

```
measure('測定する量子ビットのインデックス', '測定結果を保存する古典的レジスタのインデックス')
```

続けて次のコードを実行しましょう。

```python
backend = Aer.get_backend('qasm_simulator')
shots = 1024
results = execute(qc_qasm, backend=backend, shots=shots).result()
answer = results.get_counts()

print(answer)
```

試行回数(ショット数、デフォルトは1024)を指定し、executeの第3引数に入れます。そしてresultsの中のcountsの情報をanswerに保存します。

{'00': 481, '11': 543}

このように出てきました。
更に次のコードも実行しましょう。

```python
plot_histogram(answer)
```

![ヒストグラム1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653215/9d3507a3-0507-be79-f21c-bb26f8ccfa07.png)
この量子回路の出力状態ベクトルは$|00\rangle$と$|11\rangle$の振幅が等しいので、"$00$"と"$11$"がほぼ同数測定されているのがわかります。

### 各量子ハードウェア
IBM社がクラウド上でアクセス可能としてるマシンです。

IBM Quantum Servicesにある利用可能なマシンを選びます。最初にもやりましたね。

```python
provider = IBMQ.load_account()
provider.backends()
```

今回は"ibmq_manila"を使用しましょう。

```python
provider = IBMQ.get_provider(group='open', project='main')
backend = provider.get_backend('ibmq_manila')
```

Providerとして適当なもの(ibm-q/open/mainとなっている鍵のついていないもの)をバックエンドに使います。設定したバックエンドを引数にとって以下のコードを実行しましょう。するとジョブはマシンに送信され、IBM Quantumの自分のアカウントで確認できるようになってます(結構時間はかかります)。

```python
qc = QuantumCircuit(2, 2)

qc.h(0)
qc.cx(0, 1)

qc.measure(0, 0)
qc.measure(1, 1)

results = execute(qc, backend=backend).result()
answer = results.get_counts()

print(answer)
```

すると、このように出てきました。

{'00': 1803, '01': 133, '10': 260, '11': 1804}

IBM Quantumの方でJobを確認すると
![スクリーンショット 2022-05-03 18.32.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653215/18707fcc-2085-19d3-01b7-e916ca966651.png)
![スクリーンショット 2022-05-03 18.33.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653215/1ff1f0b7-1c86-496b-1c8c-33d874c2ae64.png)
このように詳細が書かれていますね。29分かかりました。

ここで注意したいのが、マシンのエラーがあるため"$01$"や"$10$"が少しばかり測定されてしまうということです。このエラーを軽くする方法はありますが、今回は重要でないので割愛することにします。

# 次回予告
次回は「Deutsch-Jozsa(ドイチュ・ジョサ)のアルゴリズム」について扱っていきます！
量子回路を実際に組み立てる作業をしましょう。

パート2は[こちら](https://qiita.com/Hiroki_Akita/items/37c6b503a533530cce18)から。