IBMの量子コンピュータを触ってみる

IBMは現在クラウドで量子コンピュータを利用できるサービス「IBM Quantum」を提供しています。一部のシステムについては無償で一般公開されており、誰でも量子計算を試す事できます。 

本記事では、IBMが提供している量子コンピュータ用のSDK(ソフトウェア開発キット)  「Qiskit」を用いて、IBM Quantumにアクセスする方法等について説明します。これから量子計算を学ぶ方(もちろん私も含みます)を対象とした内容になります。

（※）量子コンピューティングについては、IBMの以下の記事がとても参考になります。
https://www.ibm.com/jp-ja/topics/quantum-computing

### qiskitのインストール

Qiita内にはqiskitのインストールに関する詳細な解説記事が既にあります。  
https://qiita.com/hiro949/items/268141289cbd47c27f6a  
https://qiita.com/K-K-K-K/items/8bebc4a2556561308ccb  
https://qiita.com/ool/items/e80c1266cda68984a103  
https://qiita.com/taka2_kiara/items/796ddfb04dc974e2eb7d  

但し、IBM Quantum systemにアクセスする際、以前使われていた「qiskit-ibmq-provider」は現在では非推奨となっています。そこで、現在推奨されている  「qiskit-ibm-provider」への移行をふまえ、qiskitのインストールを紹介してみたいと思います。

(※)qiskit-ibm-providerへの移行ガイドは以下が参考になります。        
https://github.com/Qiskit/qiskit-ibm-provider/blob/main/docs/tutorials/Migration_Guide_from_qiskit-ibmq-provider.ipynb

qiskitはpython3.7以降をサポートしています。今回はAnacondaを利用し、Anacondaに含まれるcondaコマンドで仮想環境を作り、そこにqiskitをインストールすることにします。  

```sh
conda create -n qiita python=3.9　# 仮想環境の名前をqiita
conda activate qiita
pip install qiskit[all]
pip install qiskit-ibm-provider
```
私の環境はLinux Mint 20.2 x86_64でしたが、特に問題なく必要なライブラリ群がインストールされました。
 インストールされたqiskitのバージョンは

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

で確認することが出来ます。

（※）2023/5時点でのqiskitのバージョン(meta package version)は、0.43.0となっています。  
https://qiskit.org/documentation/release_notes.html 

### IBM Quantumのアカウント作成

#### 1. アカウント作成  

https://quantum-computing.ibm.com/ にアクセスします。続いて、Sign in to IBM Quantumの下方にある「Create an IBMid account」をクリックして、IBMidを作成します。

（※）アカウント作成はこちらの記事も参考になるかも知れません。  
https://qiita.com/kifumi/items/7ac33ab7939d2dd796d0

#### 2. トークンの取得  

IBMidが作成されると、IBM Quantumにサインインできます。サインインすると、実機にアクセスする際の認証に必要なAPI tokenが「****」 で表示されているので、これをコピーします。
APIトークンは、IBMProviderクラスにあるsave_account()メソッドを用いて、ディスク内に保存できます。


```python
from qiskit_ibm_provider import IBMProvider
IBMProvider.save_account(token='MY_API_TOKEN', overwrite=True)
```

APIトークンは一度保存すれば、次回以降は必要ありません。  

これでIBM Quantum systemにアクセスする準備が出来ました。

### クラウド上でIBM Quantum systemの確認

サインインができれば、既にwebブラウザ上で視覚的に量子回路を構築することが可能です。また、アプリケーションのプロトタイプの作成や、利用できるComputer resourcesも確認できます。サインイン後、webページの左上にある四角ボタン(Application switcher)があることが分かります。
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576649/9777ece2-f271-ff45-e2d0-402c9ccabb72.png)

例えば以下の操作、

```
左上ボタン(Application switcher) → Computer resources → Your resources
```

とクリックしていくと、現在のアカウントで利用できる実機（リソース）情報の一覧が取得できます。また、各リソースをクリックすると、リソースの量子ビットのレイアウトや量子ビットのエラーの詳細情報を閲覧できます。

### pythonでシステムの確認
クラウド上での操作はグラフィカルで直観的にわかりやすいですが、  Qiskitはpython環境で実行されるため、
pythonでIBM Qunatum systemのリソース情報も確認できると便利です。

#### 1. IBMProviderのインスタンス化

APIトークンを保存すれば、IBMProviderクラスをインスタンス化するだけで、保存したアカウントとデフォルトのプロジェクトを読み込むことができます。また、IBM Quantumのサービスへのアクセスは、割り当てられたIBM Quantum instancesによって制御されます。IBM Quantum instancesは、ハブ(hub)/グループ(group)/プロジェクト(project)という階層的な組織で定義されます。 一般公開されているインスタンスは ibm-q/open/main です。

```python
from qiskit_ibm_provider import IBMProvider

hub = "ibm-q"
group = "open"
project = "main"

provider = IBMProvider(instance=f"{hub}/{group}/{project}")
```

#### 2. 利用できるバックエンドの表示

利用可能な量子コンピュータリソースは、IBMProviderクラスのbackends()メソッドを用いて取得できます。


```python
provider.backends()
```

一般公開用のIBM Quantum instancesのバックエンドには、量子コンピュータ実機以外に、実機を模擬したシミュレータ(ibmq_qasm_simulatorなど)も含まれています。実機の情報のみ取得したい場合、


```python
provider.backends(operational=True, simulator=False)
```

とします。現在、以下の量子コンピュータがバックエンドとして利用可能であることが分かります。

```
[<IBMBackend('ibmq_quito')>,
 <IBMBackend('ibm_lagos')>,
 <IBMBackend('ibm_perth')>,
 <IBMBackend('ibmq_jakarta')>,
 <IBMBackend('ibmq_lima')>,
 <IBMBackend('ibmq_belem')>,
 <IBMBackend('ibmq_manila')>,
 <IBMBackend('ibm_nairobi')>]
```

#### 3. バックエンドの取得

IBMProviderクラスのget_backend()メソッドを用いると各実機リソースをバックエンドとして取得できます。  
'ibmq_lima'の場合を以下に示します。

```python
backend = provider.get_backend('ibmq_lima')
```

取得したリソースのエラーマップや量子ビットのレイアウトの情報は、qiskitのvisualizationモジュールを使うことで可視化できます

```python
from qiskit.visualization import plot_gate_map, plot_error_map, plot_circuit_layout
display(plot_error_map(backend))
```
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576649/783f9f97-a30c-2865-e923-b628a31a143d.png)


#### 4. バックエンドの詳細を確認

このままでは見づらく、また情報も少ないので、各バックエンドの詳細情報を見やすく表示したいと思います。IBMBackendクラスのメソッドを参考[※]にすると、バックエンドの詳細な情報を取得することができます。  
[※]https://qiskit.org/ecosystem/ibm-provider/stubs/qiskit_ibm_provider.IBMBackend.html#qiskit_ibm_provider.IBMBackend

例えば各バックエンドの情報を表示する以下のような関数を考えることが出来ます。
```python
def backends_detail(provider): 
    
    available_backends = provider.backends(  
        operational=True,      
        filters=lambda b: b.configuration(           
        ).n_qubits > 1 and not b.configuration().simulator)  
    
    print("{0:17}{1:<9}{2:<14}{3:<10}{4:<11}{5:<9}{6:<9}".format(
        "Backend name", "#Qubits", "Pending jobs", "Max exp.", "Max shots",
        "Version", "Processor"))     
                                                                                                                                                                         
    print("{0:17}{1:<9}{2:<14}{3:<10}{4:<11}{5:<9}{6:<9}".format(
        "--------------", "-------", "------------", "--------",
        "---------", "-------", "---------"))   
    
    for i in available_backends:             
        backend = provider.get_backend(str(i.name))                                 
        print("{0:17}{1:<9}{2:<14}{3:<10}{4:<11}{5:<9}{6:<9}".format(                                                                                                                                 
            backend.name,
            backend.configuration().n_qubits,   
            backend.status().pending_jobs, 
            backend.configuration().max_experiments, 
            backend._configuration.max_shots, 
            backend.configuration().backend_version,
            backend.configuration().processor_type['family']))   
```

先ほどのコードとまとめると
```python
from qiskit_ibm_provider import IBMProvider

hub = "ibm-q"
group = "open"
project = "main"

provider = IBMProvider(instance=f"{hub}/{group}/{project}")
backends_detail(provider)
```
となります。実行すると、

```
Backend name     #Qubits  Pending jobs  Max exp.  Max shots  Version  Processor
--------------   -------  ------------  --------  ---------  -------  ---------
ibm_perth        7        100           300       100000     1.2.7    Falcon   
ibmq_quito       5        3             300       100000     1.1.41   Falcon   
ibmq_belem       5        1             300       100000     1.2.8    Falcon   
ibmq_manila      5        11            300       100000     1.2.3    Falcon   
ibm_lagos        7        158           900       100000     1.3.3    Falcon   
ibm_nairobi      7        189           300       100000     1.3.3    Falcon   
ibmq_jakarta     7        35            300       100000     1.4.0    Falcon   
ibmq_lima        5        1             300       100000     1.0.48   Falcon 
```
と出力されます。利用可能なリソース情報の一覧が得られました。
一覧の中の#Qubitsは量子ビットの数、Pending jobs は現在の待ち行列数です。

## IBM Quantum systemへジョブを投入する

最期にGHZ状態を生成する量子回路をqiskitで作り、それを計算するジョブをIBM Quantum systemに投入してみます。ジョブの投入に関しては、以下の記事を参考にしています。  
https://qiskit.org/documentation/locale/ja_JP/intro_tutorial1.html

GHZ状態は、5量子ビットの場合、例えば以下の量子回路で生成されます。

```python
from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator
from qiskit.visualization import plot_histogram

qc = QuantumCircuit(5,5)
qc.h(1)
qc.cx(1,2)
qc.cx(1,0)
qc.cx(2,3)
qc.cx(3,4)
qc.measure([0,1,2,3,4], [0,1,2,3,4])
qc.draw('mpl')
```

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576649/e4bab387-1acd-7c7e-dcae-d9bd395ec6c3.png)


上記の量子回路を量子コンピュータで実行させる前に、シミュレータで確認します。

```python
# simulator
simulator = AerSimulator()
compiled_circuit = transpile(qc, simulator)

job = simulator.run(compiled_circuit, shots=1000)
result = job.result()

counts = result.get_counts(compiled_circuit)
print(counts)
plot_histogram(counts)
```
ここではshots=1000として、1000回の観測を実施しました。
結果は以下のようになりました。正しく全ての量子ビットがエンタングルしているようです。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576649/c82354e2-0b8c-8d17-122e-6992ea534c54.png)


次に実機を用いて同じ量子計算を実施したいと思います。実機へジョブを投入する場合は、上記のコードでsimulatorを実機のbackendに変更するだけです。ここでは仮にbackendにibmq_limaを選択します。

```python
# IBM Quantum system

backend = provider.get_backend('ibmq_lima')
compiled_circuit = transpile(qc, backend)

job = backend.run(compiled_circuit, shots=1000)
job_id = job.job_id()

print(job_id)
```

コードに間違いが無ければ、ジョブがibmq_limaへ投入されます。もし多くの待ち行列が発生している場合は、job_idを用いて後日に結果を回収することができます。ジョブのstatusは

```python
retrieve_job = provider.retrieve_job(job_id)
retrieve_job.status()
```
にて確認可能です。計算が無事終了すると、

```
<JobStatus.DONE: 'job has successfully run'>
```
と表示されます。job_id情報から、計算結果を以下のように回収します。

```python
retrieve_job = provider.retrieve_job(job_id)

result = retrieve_job.result()
counts = result.get_counts(compiled_circuit)
print(counts)
plot_histogram(counts)
```

結果は以下のようになりました。シミュレータの結果と違って少し他の状態も観測されています。特に'01111'が誤って多く観測されているようです。つまり、本来"1"であるはずの5番目の量子ビットが"0"になっています。(qiskitはlittle endian表示ですので、左から右へ、量子ビット番号の大きい順に量子ビットを並べます) これらは実機の量子ビットのエラーや量子ビットのレイアウトなどに関係しているかもしれません。  
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576649/83d00561-a981-ee2d-cad5-3f85a9053e2f.png)

 
### まとめ

今回、qiskitを用いたIBM Quantum systemへのアクセスを説明しました。何かしら参考になれば幸いに思います。初学者ですので、間違いなどありましたらご指摘下さると幸いです。今後はqiskit runtimeについても記事を書ければと思います。