mitiqを使った誤り緩和

:::note warn
mitiqはGPLv3ライセンスで配布されています。商用利用の際は十分にご注意ください。
また、そのため、本記事に記載したコードは、GPLv3で再配布可能とします。
:::

# mitiqとは
mitiqとは、現在や近い将来の量子コンピュータのための、誤り緩和(error mitigation)の方法を提供するPythonツールキットです。

誤り緩和の手法はいくつか提案されていますが、mitiqには複数種類の手法が実装されています。本記事ではそれらを見ていきます。

また、本記事でいう量子コンピュータとは、量子ゲートを並べて量子回路を作る量子ゲート方式の量子コンピュータを指すものとします。

# 誤りと誤り緩和
通常のコンピュータ(古典コンピュータ)が、内部状態を電圧の高低や電荷の有無などの比較的安定した物理状態で保持するのに対し、量子コンピュータの内部状態は、超伝導素子やイオン、原子などのスピンといった、外乱に弱い物理状態で保持されます。
量子コンピュータの計算途中で、内部状態が壊れると、計算の誤り(error)が発生します。誤りが多くなりすぎると、計算結果はランダムなビットとほとんど見分けがつかなくなります。そのため、今の量子コンピュータでは、誤りが多くならない範囲の小規模な量子回路しか動かすことができず、それが現在大きな課題となっています。

誤りを克服する手法には、まず、量子誤り訂正符号が挙げられます。この手法では、多くの量子ビットをまとめて一つの量子ビットとして符号化し、計算の誤りを発見し修正します。この手法は大変強力ですが、実現のためには高精度な多数の量子ビットを持った量子コンピュータが必要です。最近は小規模な量子誤り訂正符号が実験的に実装されつつありますが、誤りを克服するには、より多数の量子ビットをまとめた強力な符号を作る必要があります。

それとは別の手法として、誤り緩和があります。この手法では、量子誤り訂正符号のような符号は作らず、よって、誤りは解消されないのですが、その影響を軽減することを目指します。

# Zero-Noise Expolation (ZNE)
ここでは、確率や期待値などの実数値を量子回路のサンプリングで求める問題を想定します。
例えばVQEによる量子化学計算などがその事例に相当します。

## 原理
量子回路は、短いと誤りが少ないですし、長いと誤りは多くなります。
極論を言ってしまえば、量子回路の長さがゼロだと誤りがないはずです。ですが、量子回路を短くすることは困難ですし、限界もあります。

しかし、同じ量子回路を無駄に長くすることはできます。
量子回路は、逆演算を考えることができるので、量子回路を$G$, その逆を$G^\dagger$としたとき、

$G = G G^\dagger G$

が成立します。同様に、$n$を適当にとったとき

$G = G (G^\dagger G)^n$

も成立します。そこで、本来求めたい量子回路だけでなく、無駄に長くした量子回路もサンプリングを行います。そして、その結果を元に、量子回路の長さがゼロの場合の結果を外挿し、その値を誤りの影響がない場合の値とみなします。

mitiqのドキュメントにある図を引用します。
![result_from_readthedocs](https://mitiq.readthedocs.io/en/latest/_images/zne-3-options_42_0.png)

Noise scale factorが、量子回路を何倍に引き伸ばしたかに相当し、1.0のものが元々動かしたい量子回路です。この図のように、Noise scale factorが0のときの値を、誤りの影響がない場合の値だと考えます。

# Probablistic Error Cancellation (PEC)
この手法には、量子コンピュータがどういう誤りを起こすのかといったノイズモデルを予め知っておく必要があります。
その他の問題設定はZNEと似ています。

## 原理
各ゲートについて、誤りがない場合にこう動くはずだ、という理想形と、ノイズモデルから導かれる、実際にはこう動くだろうという形を考えます。
そうすると、理想のゲートを、実際のゲートを組み合わせた線型結合で書き表すことができます。これらの量子回路の組み合わせの中から、量子回路をサンプリングし、それを実行した結果の重み付き和を、理想の量子回路を実行した結果とみなします。


# Clifford Data Regression (CDR)
本手法も、確率や期待値などの実数値を求める問題を想定しています。

## 原理
Cliffordゲートと呼ばれる、CNOTゲートやHゲート、パウリゲートを含むいくつかのゲートは、古典コンピュータで誤りなしに高速にシミュレーションできることが知られています。そこで、Cliffordゲートからなる量子回路の、実際の実行結果と、誤りのないシミュレーション結果とをデータセットにして、実際の実行結果から誤りのない結果を推測する機械学習モデルを作ります。
量子回路の実行結果を、その機械学習モデルに入力し、得られた推測値を、誤りを取り除いた実行結果とみなします。



# Digital Dynamical Decoupling (DDD)
この手法は他のものとかなり毛色が異なります。他の手法のように期待値や確率などの実数を推定するものではなく、元の量子回路と全く等価であって、量子コンピュータで動かした際により誤りが少ないであろう量子回路に変換を行います。

## 原理
量子コンピュータのハードウェアにおいて、ゲート操作は、マイクロ波などのパルスを量子ビットに当てることで実現されています。そして、パルスを周期的に当てていると、外乱の影響が減るようです。この手法をDynamical Decoupling (DD)といいます。
本来はそれは量子回路作成よりも低レイヤーの技術と考えられますが、DDDでは、そのようなパルスに合成されるであろう量子回路を作り出すことで、量子回路やゲートのレベルでのDDを実現しようとしています。

# Qiskitとmitiqでの誤り緩和
mitiqはQiskitの他、Cirq, pyQuil, Braket, PennyLaneをサポートしていますが、本記事ではQiskitによる実装を示します。
そのために、Qiskitでのノイズのある量子回路のシミュレーション手法をみます。

### ノイズモデルの取得と誤りのあるシミュレータの構築
IBMの無料版アカウントを使って、量子コンピュータの校正データをもらってきます。
それを元に、シミュレータのインスタンスを構築します。

```py
from qiskit import IBMQ
from qiskit.visualization import plot_histogram
from qiskit.utils import QuantumInstance
from qiskit.providers.aer import AerSimulator
from qiskit.providers.aer.noise import NoiseModel

# 利用にはユーザ登録とトークンの取得・保存が必要です
# IBMQ.save_account(token)
provider = IBMQ.load_account()
manila = provider.get_backend('ibmq_manila')
noise_model = NoiseModel.from_backend(manila)

# シミュレータとノイズモデルから、誤りのあるシミュレータを構築します
backend = AerSimulator()
noisy = QuantumInstance(backend=backend, shots=100000, basis_gates=noise_model.basis_gates, noise_model=noise_model)
```

### 量子回路

今回は、こんな量子回路を作ってみました。

```py
qc = QuantumCircuit(2)
qc.x(1)
qc.cx(1, 0)
qc.rz(0.912, 0)
qc.cx(1, 0)
qc.ry(0.5, 1)
qc.rz(-1.82, 1)
qc.ry(1.0, 1)
qc.cx(1, 0)
qc.rz(1.82, 0)
qc.cx(1, 0)
qc.ry(0.5, 1)
qc.cx(1, 0)
qc.rz(-0.912, 0)
qc.cx(1, 0)
qc.ry(1.57, 1)
qc.measure_all()
# 量子回路を描画
qc.draw(output='mpl')
```

![描画された量子回路](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88256/a0d79142-254f-be20-64d0-40de83413032.png)

この量子回路の実行結果は、誤りのないシミュレータでは以下のようになります(誤りがなくても確率的ではあるので、動かすたびに結果は変わります)

```py
plot_histogram(execute(qc, backend=backend, shots=100000).result().get_counts())
```

![上記コードの実行結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88256/5c5d60a1-7041-83c6-8c78-7f47d1ab2d08.png)

また、誤りを入れたシミュレータでは以下のようになります。

```py
plot_histogram(noisy.execute(qc).get_counts())
```

![上記コードの実行結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88256/67d7535d-30e7-5dcf-a846-5a6305b4f7c8.png)

今回は、|00>が出る確率を、誤り緩和により推定します。

## mitiq.zneを動かす
ZNEは、回路を無駄に大きくして誤りが増えた場合の結果から、誤りがない場合の結果を外挿するものでした。

```py
from mitiq.interface import convert_to_mitiq
frontend = "qiskit"
from mitiq import zne
```

量子回路を大きくすると、|00>が出る確率が減っていく様子がわかります。

**2倍** 
```py
plot_histogram(noisy.execute(zne.scaling.fold_gates_at_random(qc, scale_factor=2)).get_counts())
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88256/cbc457a1-2c11-1765-6cbc-396348cf8b9b.png)

**3倍**
```py
plot_histogram(noisy.execute(zne.scaling.fold_gates_at_random(qc, scale_factor=3)).get_counts())
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88256/fb26082f-e1c4-2c9c-4ccc-3810f2ba2d7c.png)

**4倍**
```py
plot_histogram(noisy.execute(zne.scaling.fold_gates_at_random(qc, scale_factor=4)).get_counts())
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88256/8e1ccc57-45e6-539d-e557-f90f5ce654df.png)

ZNEによる推定は、以下でできます。

```py
def execute(circuit):
    counts = noisy.execute(qc).get_counts()
    try:
        return counts['00'] / counts.shots()
    except KeyError:
        return 0.0

factory = zne.inference.RichardsonFactory(scale_factors=[1.0, 2.0, 3.0, 4.0])
zne_result = zne.execute_with_zne(qc, execute, factory=factory)
print(zne_result)
_ = factory.plot_fit()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88256/6a9d37e0-02db-1f0b-373b-87ff10c5bf63.png)

`scale_factors` が、「何倍にするか」のパラメータです。`RichardsonFactory` は、フィッティング方法の種類で、今回はmitiqデフォルトのものを使っています。

ちなみに、今回はインチキをしていて、いい結果が出るまで何度か回しました。
あまりうまくいかなかった原因として、計算中の誤りだけではなく、測定の誤りの影響も受けている可能性が考えられます。

## mitiq.pecを試す
ノイズモデルをmitiqの形式で書かないといけないのですが、それが分からなかったので、CNOTの誤りの確率を適当に置いて、depolarizing error (量子ビットがある確率で消えてしまう誤り。ブロッホ球が小さくなる図で書かれることが多い)を仮定します。

```py
from mitiq import pec
from mitiq.pec.representations.depolarizing import represent_operation_with_global_depolarizing_noise
qc_ = QuantumCircuit(2)
qc_.cx(1, 0)
reps = [represent_operation_with_global_depolarizing_noise(qc_, noise_level=0.04)]
pec.execute_with_pec(qc, execute, representations=reps, num_samples=100)
```

CNOTの誤りの確率を4% (0.04)で置いた(かなり多いのでは?)のですが、そうすると|00>の出現確率が0.97になりました。それは改善されすぎている気がします。

ちょっと値を振ってみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88256/f2826c36-0c4e-6889-2dcd-994684fb1fd1.png)

CNOTをdepolarizing errorと仮定するだけの雑なノイズモデルなので、どれも合っていないのですが、パラメータの振り具合でこれだけ値が変わるのは怖いようにも思いました。
おそらく、実際のCNOTの誤り率は1〜2%で、一方で、1量子ビットゲートの誤りも完全には無視できないのではないでしょうか。
一方で、ノイズモデルをきちんと表現できるのであれば、有用なように見えます。

## CDR
今回使っている量子回路は、クリフォードゲートをあまり含まないので、難しいのではないかと思います。ドキュメントがcirq前提で書かれていることもあり、スキップします。

## DDD
これは、単なる量子回路のシミュレータでは、よくはならないのではないかと思います。一応動かしてみると

```py
from mitiq import ddd
rule = ddd.rules.yy
ddd.execute_with_ddd(
    circuit=qc, 
    executor=execute, 
    rule=rule,
)
```

の結果が0.8548となり、誤りのあるシミュレータで普通に動かしたときとあまり変わりません。また、今回使っている量子回路では、2量子ビットどちらもそれなりに動いているので、あまりゲートを挿入する余地もありません。変換後の量子回路を一応描くと、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88256/da7548cc-08a0-fd04-b743-dbaefea79444.png)
となりました。少しだけYが入っています。


# まとめ
mitiqは量子誤り緩和を行うツールキットです。量子コンピュータは計算の誤りが大きな課題となっていますが、量子誤り緩和によって、その影響を軽減することができます。
いくつかの方法が実装されていますが、今回試してみて、以下のように感じました。

- ZNEは、量子回路をわざと水増しして、その挙動を見て、誤りがない場合の値を推定します。この手法は、水増ししたときの挙動を可視化できるのはいいように思いました。しかし、動かすたびに値が大きく変わったので、何度か動かして本当に妥当な結果かを見た方がよさそうです。
- PECは、ノイズモデルを仮定して、それを量子回路で表現し、サンプリングをすることで、誤りがない場合の値を推定します。ノイズモデルを適切に仮定できて、それが量子回路で表現できるのであれば、とても強力な手法のように見えます。一方で、今回は、ノイズモデルをうまくmitiqのものに落とし込めなかったので、非常にいい値が出るパラメータは見つかったものの、妥当なのか分かりませんでした。
- CDRは、クリフォードゲートを厳密にシミュレーションしたデータを用いて機械学習をします。今回実行しませんでしたが、クリフォードゲートが多い回路では有効なように思います。
- DDDは、シミュレータでは検証しづらそうですが、本物の量子コンピュータを使う場合は試してみてもいいかもしれません。不必要な操作をいっぱい入れる余地があるような回路では有効かもしれません。

# 参考文献
https://mitiq.readthedocs.io/en/latest/guide/guide.html
https://github.com/unitaryfund/research/blob/main/nepec/mitiq-qiskit-pec-nepec/mitiq-qiskit-zne-pec-nepec.ipynb