Qiskit 機能色々(TIPS)

# QISKIT 機能色々(TIPS)

主に自分用のメモです。
コマンドの列挙ですので、同時に動かない機能があります。

- 状態ベクトル系では、回路にmeasureがあるとバグる。（正確には、射影測定後の状態ベクトルが得られる)
- 古典レジスタが存在すると使えない機能がある

# install
```python
pip install qiskit pylatexenc
```

# import
```python

#必要なモジュールのインポート
from qiskit import IBMQ, QuantumCircuit, ClassicalRegister, QuantumRegister
from qiskit import execute, Aer
from qiskit.qasm import pi
from qiskit.tools.visualization import plot_histogram, circuit_drawer, plot_gate_map, plot_circuit_layout
from qiskit import IBMQ, transpile
from qiskit.providers.ibmq.managed import IBMQJobManager
from qiskit.quantum_info.operators import Operator, Pauli
from qiskit.quantum_info import Statevector
from qiskit.aqua.utils import tensorproduct
from qiskit.quantum_info.analysis import average_data
from qiskit.aqua import QuantumInstance
from qiskit.aqua.operators import PauliExpectation, CircuitSampler, StateFn
from qiskit.aqua.operators import X, Y, Z, I
from qiskit.circuit.library import RYGate
from qiskit.aqua.operators.state_fns import CircuitStateFn
from qiskit.circuit.random import random_circuit
from qiskit.circuit.add_control import add_control
from qiskit.providers.aer import QasmSimulator

import numpy as np
```

# IBMQロード

```python
## "MY_API_TOKEN" に、上で取得したtokenを入れる
from qiskit import IBMQ
IBMQ.save_account('XXXXXXXX',overwrite = True)

# least busy な バックエンドを知る
from qiskit.providers.ibmq import least_busy

# 自分のアカウント情報をloadする。（あらかじめ IBMQ.save_account を実行しておく必要がある. 複数のアカウントを使い分ける時はここで行う)
provider = IBMQ.load_account()

# 自分のアカウントで使用できるバックエンドを見る
provider.backends()

# Run our circuit on the least busy backend. Monitor the execution of the job in the queue
from qiskit.tools.monitor import job_monitor
```

# オブザーバブルの作り方

```python
# Observableを作る　(方法その1)
pauli0 = Pauli(label='II')
pauli1 = Pauli(label='IZ')
obs_op = Operator(pauli0) + Operator(pauli1) #O = II+IZ
obs1 = obs_op.data # from operator to matrix
print(obs1)
# Observableを作る　(方法その2)
I_mat = np.array([[1, 0], [0, 1]])
Z_mat = np.array([[1, 0], [0, -1]])
obs2 = tensorproduct(I_mat,I_mat) + tensorproduct(I_mat,Z_mat) # matrix

# Observableを作る　(方法その3)
obs3 = np.array([[2,0,0,0],[0,0,0,0],[0,0,2,0],[0,0,0,0]])

# Observableを作る　(方法その4)
obs4 =  1*I^I + 1*I^Z

# Observableを作る　(方法その5)
 from qiskit.aqua.operators import WeightedPauliOperator
 pauli_dict = {
      'paulis': [{"coeff": {"imag": 0.0, "real": 1}, "label": "II"},
                {"coeff": {"imag": 0.0, "real": 1}, "label": "IZ"},                             
                ]
  }
obs5 = WeightedPauliOperator.from_dict(pauli_dict).to_opflow()

```

ちなみに、方法4は若干の注意が必要です。
`obs4 =  2*I^I + 2*I^Z` はOKですが、
`obs4 =  2*(I^I + I^Z)` はだめです。
(エラー無く通りますが、期待値計算の際に定数倍が無視されます）

なんだかんだ、汎用性が高いのは方法5だと思います。
obs5.to_matrix() で配列形式にも出来ます。

# 回路準備

```python
#　回路の定義
nqubits = 2 #qubits
ncbits = 1  #classical bits
qr = QuantumRegister(nqubits)
cr0 = ClassicalRegister(ncbits)
cr1 = ClassicalRegister(ncbits)
circuit = QuantumCircuit(qr,cr0,cr1) 
# レジスタに興味ない場合は QuantumCircuit(nqubits)や QuantumCircuit(nqubits,ncbits) でOK
#circuit.draw('mpl')
```

# 回路記述いろいろ

```python
# 回路記述

# Define an arbitrary unitary gate
U_mat = np.array([[0,1],[1,0]])

#Note : this operation is invalied in ibmq real quantum devices
#circuit.reset(0) #reset 0th qubit as |0>
#circuit.reset(1) #reset 1st qubit as |0>
#circuit.initialize([0, 1],0) #set 0th qubit as [0,1] = |1>
circuit.h(0)
circuit.h(1)
#circuit.x(0).c_if(cr1,1)　# Note : Only 1 classical bit register can be used in c_if.
#circuit.unitary(U_mat,0)
#circuit.x([0,1]) #add X-gate from 0th to 1st qubit.
#circuit.cx(0,1) #Controlled-NOT. The controlled and target bit is 0th and 1st one, respectively.
#circuit.rz(2,0) ## RZ(a)=exp(-i*a/2*Z)に注意. rz(rotation_angle, qbit_label)
#circuit.hamiltonian(obs1,10,[0,1]) #Hamitonian evaluation ?
psi = CircuitStateFn(circuit) # get final state vector. Note:The classical register shouldn't be used.
#circuit.measure_all() #Note : don't add measurement when you use statevector_simulator
circuit.draw('mpl')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878664/c1bbb6e5-ec64-224c-011a-43ebe179d645.png)

psi = CircuitStateFn(circuit) を使う場合は、
-古典レジスタを使わない
-measureを使わない
が必要になります。

## 任意制御ゲートの作成

```python

theta_01 = np.arctan(1)
Ugate_01 = RYGate(2*theta_01)
Ugate_01_ctrl = add_control(Ugate_01, num_ctrl_qubits=2, label='CU_01', ctrl_state='01')

def eigen_rot(circ):
    circ.append(Ugate_01_ctrl,[0,1,3])
```


## 回路の途中の状態ベクトルをメモる

回路の途中の状態ベクトルを取り出すことが出来ます。
この方法はシミュレーター限定ですが、statevector simulatorに限らず、qasm simulatorでも使えます。
回路の任意の位置で　snapshot という操作を入れます。（ゲートのような感覚です）
`circuit.snapshot('my_sv',snapshot_type='statevector') # get a snapshot of present statevector`
snapshotはいくつでも入れられます。
次に、回路を実行します。
`results = execute(circuit, backend, shots=1).result()`
結果の変数resultsの中身に、以下の呪文でアクセスします。
`statevec = results.data()['snapshots']['statevector']['my_sv'][k] # 2^nqubits complex vector |ψ>`
ここで、kはsnapshotを複数とった場合に何番目を取り出すかを意味します。
出力結果は複素ベクトル（状態ベクトル）になっていますので、期待値計算は簡単です。
`expectation = np.real(np.vdot(statevec,np.dot(obs_mat,statevec))) # Real[<ψ|O|ψ>]`
obs_matはオブザーバブルの行列表現です。

## c_ifの使い方
**2021/01/02追記**
c_ifは1bit古典レジスタしか引数に取れません。以前は複数bitが出来たようです。
**複数bitでも実行可能に見えますが、実際には何も操作されなかったりします。ひどい。。**
例えば、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878664/56e4f2bb-7947-b34e-4793-96af1c134d3c.png)

は正常に動きます("11")が、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878664/a03afde1-dd60-c1de-2c2b-905018e61109.png)

は正常に動きません("10")。なんでや！　たぶん最初の古典ビットだけがc_ifの対象ぽいですね。

また、複数古典ビットを一気に渡す
`cr = ClassicalRegister(2), circuit.x(0).c_if(cr,3)` も、無効です。以前は出来た模様。

1 classical bit register をたくさん定義するのは面倒です。
`circuit.x(0).c_if(cr[0],1)` と書きたい。その場合は以下のようにします。

```python
qr = QuantumRegister(2)
circuit = QuantumCircuit(qr) 
cr = [ ClassicalRegister(1) for _ in range(2) ]
for register in cr:
    circuit.add_register( register )
circuit.x([1])
circuit.measure(1,1)
circuit.x(0).c_if(cr[1],1)
circuit.measure(0,0)
```

また、c_ifは実機では動かないそうです。以下の5-1を見てください。
https://qiskit.org/textbook/ch-algorithms/teleportation.html#testing

# 回路を調べる

```python
# depth of circuit
circuit.depth()

## decompose into hardware gates
circuit.decompose().draw('mpl')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878664/33b330a5-0292-d45d-a14c-6314b2f58881.png)



# backendの定義

```python
# backendの定義
#backend = Aer.get_backend('qasm_simulator')
#backend = Aer.get_backend('statevector_simulator')
#backend = Aer.get_backend('unitary_simulator')
#backend = provider.get_backend('ibmq_santiago')
```

# transpile関係
```python
#ibmq information

#transpile
circuit_transpiled = transpile(circuit, backend=provider.get_backend('ibmq_santiago'), optimization_level=3)
plot_circuit_layout(circuit_transpiled, backend=provider.get_backend('ibmq_santiago'))
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878664/6d4de9c7-1a49-2dad-3454-7c94420413de.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878664/a143a346-ca72-9b23-4a7e-74d56a527452.png)


## IBM実機プロパティを取る

```python
# T1/T2 info.
backend = provider.get_backend('ibmq_santiago')
props = backend.properties()
def describe_qubit(qubit, properties):
    """Print a string describing some of reported properties of the given qubit."""

    # Conversion factors from standard SI units
    us = 1e6
    ns = 1e9
    GHz = 1e-9

    print("Qubit {0} has a \n"
          "  - T1 time of {1} microseconds\n"
          "  - T2 time of {2} microseconds\n".format(
              qubit,
              properties.t1(qubit) * us,
              properties.t2(qubit) * us))
              #properties.gate_error('u2', qubit),
              #properties.gate_length('u2', qubit) * ns,
              #properties.frequency(qubit) * GHz))

describe_qubit(0, props)
describe_qubit(1, props)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878664/1a25e630-3ed2-59b9-0f84-bbbf8a39c5d4.png)

## qubitアサイン手動設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878664/8c14c5c5-e65e-ad5f-3605-7f3208612da4.png)

```python
# Virtual -> physical
#    0    ->    3
#    1    ->    4
#    2    ->    2

circuit_opt = transpile(circuit, backend=provider.get_backend('ibmqx2'), initial_layout=[3, 4, 2])
plot_circuit_layout(circuit_opt, backend=provider.get_backend('ibmqx2'))
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878664/38b11d4a-80a5-d0c2-ce51-fca7d2ee6974.png)

#execute関連

## IBMQ認証

"MY_API_TOKEN" に、IBMQ HPで取得したtokenを入れる

```python
from qiskit import IBMQ
IBMQ.save_account('MY_API_TOKEN',overwrite = True)

# 自分のアカウント情報をloadする。（あらかじめ IBMQ.save_account を実行しておく必要がある. 複数のアカウントを使い分ける時はここで行う)
provider = IBMQ.load_account()

# 自分のアカウントで使用できるバックエンドを見る
print(provider.backends())

# 実行（実行状況のモニタアリ）
from qiskit.tools.monitor import job_monitor
job= execute(circuit, backend=provider.get_backend('ibmq_qasm_simulator'), shots=8192) #ibmq
results = job.result()
counts=results.get_counts()
plot_histogram(counts)

```

## 単一jobのexecute
```python
# execute on qasm simulator
results = execute(circuit, backend=Aer.get_backend('qasm_simulator'), shots=1024).result()

# execute on ibmq
#job= execute(circuit, backend=provider.get_backend('ibmq_santiago'), shots=1024)
#job_monitor(job, interval=2)
#results = job.result()
#counts = results.get_counts()

# execute on state vector simulator
#results = execute(circuit, backend=Aer.get_backend('statevector_simulator'),shots=1).result()
#results.get_statevector()
```

## 過去のjobの回収

指定したマシンで直近10個の実行結果のIDを取る
```python
[x.job_id() for x in provider.get_backend('ibmq_qasm_simulator').jobs(limit=10)]
```

指定したID"hogehoge"の結果を回収する
```python
provider.get_backend('ibmq_qasm_simulator').retrieve_job("hogehoge")
```

## jobの実行上限を確認

```python
backend_job_limit = backend.job_limit()
remain_jobs = backend.remaining_jobs_count()
maximum_jobs = backend_job_limit.maximum_jobs
print('Job is {:} / {:}'.format(remain_jobs, maximum_jobs))
```
`Job is 5 / 5`

## error mitigation

```python
from qiskit.aqua import QuantumInstance
# Import measurement calibration functions
from qiskit.ignis.mitigation.measurement import (complete_meas_cal, tensored_meas_cal,CompleteMeasFitter, TensoredMeasFitter)
qi_noise_model_ibmq = QuantumInstance(backend=provider.get_backend('ibmqx2'), shots=8000, measurement_error_mitigation_cls=CompleteMeasFitter, measurement_error_mitigation_shots=8000)
job=qi_noise_model_ibmq.execute(circuit_opt)
results = job # q_instance.execute の戻り値は、既に.resultなので注意。
counts=results.get_counts()
plot_histogram(counts)
```


## noise model を使ったexecute

```python
#Noise model
from qiskit.providers.aer.noise import NoiseModel
backend_dev = provider.get_backend('ibmqx2')
coupling_map = backend_dev.configuration().coupling_map
noise_model = NoiseModel.from_backend(backend_dev.properties())
q_instance = QuantumInstance(backend=Aer.get_backend("qasm_simulator"), shots=8192, noise_model=noise_model,  coupling_map=coupling_map, measurement_error_mitigation_cls=CompleteMeasFitter, cals_matrix_refresh_period=30)
job=q_instance.execute(circuit)
results = job
counts=results.get_counts()
plot_histogram(counts)
```

または、

```python
  backend_dev = provider.get_backend('ibmqx2')
  coupling_map = backend_dev.configuration().coupling_map
  basis_gates =  backend_dev.configuration().basis_gates
  noise_model = NoiseModel.from_backend(backend_dev.properties())
  print(noise_model)  
  results = execute(circs,backend=backend,   noise_model=noise_model, coupling_map=coupling_map, basis_gates=basis_gates).result()  
```

または、

```python
ibmqx2_simulator = QasmSimulator.from_backend(provider.get_backend('ibmqx2'))
results = execute(circs,backend=ibmqx2_simulator).result()
```


## 複数jobの同時execute


```python
 # Build ten circuits.
circs = []
for _ in range(10):
    circs.append(random_circuit(n_qubits=5, depth=4))

results = execute(circs,backend=backend).result()
results.get_counts(5)  # Counts for experiment 5.
```

または、

```python

 # Build ten circuits.
circs = []
for _ in range(10):
    circs.append(random_circuit(n_qubits=5, depth=4))

job_manager = IBMQJobManager()
job_set_foo = job_manager.run(circs, backend=backend, name='foo')
results = job_set_foo.results()
results.get_counts(5)  # Counts for experiment 5.
```

IBMQ job manager を使う場合、transpileがすっ飛ばされる。
そのため、特定のゲートの回路しか通らない。
これを回避するには、手動transpileしてから投げればOK。(backend指定transpileは、やや長めの時間がかかります）
`circs = transpile(circs, backend)`
https://qiskit.org/documentation/stubs/qiskit.providers.ibmq.managed.IBMQJobManager.html


# 測定結果の加工
```python
results.get_memory(circuit) #全shotの結果を列挙
counts = results.get_counts()
counts.most_frequent() # 頻度最大のbit列を取得
counts.get('00') # '00'の観測された回数を取得
```

# 期待値計算

```python
#Calc. expectation

# qasm_simulator or ibmq

#方法その1 , Note : obs should be diagonalized in Z basis
#counts = results.get_counts()
#expectation = average_data(counts, obs)

#方法その2
q_instance = QuantumInstance(backend=Aer.get_backend('qasm_simulator'), shots=8192)
# define the state to sample
measurable_expression = StateFn(obs4,is_measurement=True).compose(psi) 
# convert to expectation value
expectation = PauliExpectation().convert(measurable_expression)  
sampler = CircuitSampler(q_instance).convert(expectation)    # get state sampler (you can also pass the backend directly)
Expectation = sampler.eval().real

# state vector simulator
#result_statevec = Statevector(results.get_statevector())
#expectation = np.real(result_statevec.expectation_value(Operator(obs1)))
```

# plot関連

## ヒストグラムplot

```python
plot_histogram(counts)
```

### 確率0のcountsも表示する

```python

# zero-padding
for i in range (2**nqubits):
    counts.setdefault(format(i, '0'+str(nqubits)+'b'),0)

plot_histogram(counts, figsize = [20,5])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878664/1fc67961-551d-c256-d8dd-060622141ecd.png)


### statevector simulator を使ったときのヒストグラムplot

```python
n = nqubits
results = execute(circuit, backend=Aer.get_backend('statevector_simulator'), shots=1).result()
final_state = results.get_statevector()

## z方向に観測した時の確率分布を求める. (状態ベクトルの各成分の絶対値の二乗=観測確率)
probs = np.abs(final_state**2)
print(probs)
# プロットする
import matplotlib.pyplot as plt
%matplotlib inline

## z方向に射影測定した時に得られる可能性があるビット列
z_basis = [format(i,"b").zfill(n) for i in range(probs.size)]

plt.figure(figsize=(10, 5))
plt.xlabel("states")
plt.ylabel("probability(%)")
plt.bar(z_basis, probs*100)
plt.show()
```

Statevectorクラスのメソッドを使っても出来るかもしれません。

## 回路プロットの横幅変更

改行したくないときはfoldの値を増やす。

```python
circuit.draw('mpl', fold = 30)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878664/0d0a7ba5-d686-18ce-74e9-ad030fb6c355.png)

更に、 scale = 0.5 等で画像サイズを変えられる。

## matplotlibによるグラフ

```python
"""複数のグラフを重ねて描画するプログラム"""
import matplotlib.pyplot as plt

fig, ax = plt.subplots()


c1,c2 = "blue","green"     # 各プロットの色
l1,l2 = "ibmq_santiago","simulator"   # 各ラベル

ax.set_xlabel('time')  # x軸ラベル
ax.set_ylabel('magnetization')  # y軸ラベル
ax.set_title('Evolution of magnetization') # グラフタイトル
# ax.set_aspect('equal') # スケールを揃える
ax.grid()            # 罫線
#ax.set_xlim([-10, 10]) # x方向の描画範囲を指定
#ax.set_ylim([0, 1])    # y方向の描画範囲を指定
ax.plot(x, y, color=c1, label=l1, marker="o")
ax.plot(x_sim, y_sim, color=c2, label=l2)

ax.legend(loc=0)    # 凡例
fig.tight_layout()  # レイアウトの設定
# plt.savefig('hoge.png') # 画像の保存
plt.show()
```

# qiskit.aqua

## QAOA

```python
# Weighted matrix. 
w = np.array([
    [0, 1, 0, 1],
    [1, 0, 1, 0],
    [0, 1, 0, 1],
    [1, 0, 1, 0]
])

p = 1
backend = Aer.get_backend('statevector_simulator')
optimizer = COBYLA()
qubitOp, offset = max_cut.get_operator(w)

qaoa = QAOA(qubitOp, optimizer, p)
quantum_instance = QuantumInstance(backend)
result = qaoa.run(quantum_instance)


Statevec = Statevector(result.eigenstate)
counts = Statevec.probabilities_dict()
plot_histogram(counts)
```

# その他

## circuit.remove_final_measurement() の意義
動作がよくわかりません。
古典レジスタも一緒に消してしまって、回路として使えなくなってしまうような。

## 状態ベクトルシミュレーターのときはmeasureを無効化するオプションがほしい

## QPEのライブラリ
https://qiskit.org/documentation/stubs/qiskit.circuit.library.PhaseEstimation.html