雑音と密度行列¶
run() に noise= を渡すと、回路は密度行列
シミュレーションに切り替わり、各ゲートの直後にチャネルが適用されます:
from blueqat import Circuit
from blueqat.noise import depolarizing
rho = Circuit(2).h[0].cx[0, 1].run(noise=depolarizing(0.01))
noise= を渡さなくても run(backend='density') で密度行列バックエンドを
使えます(純粋状態の |psi><psi| が返ります)。
チャネル¶
from blueqat.noise import (depolarizing, pauli_depolarizing,
amplitude_damping, phase_damping, kraus)
depolarizing(p) # (1-p) rho + p I / 2**k
pauli_depolarizing(p) # (1-p) rho + (p/3)(X rho X + Y rho Y + Z rho Z)
amplitude_damping(gamma) # |1> が |0> へ減衰 (T1)
phase_damping(lam) # エネルギーを失わずコヒーレンスだけ失う (T2)
kraus([k0, k1, ...]) # Kraus演算子を直接渡す任意のチャネル
depolarizing() は Nielsen & Chuang の定義です(確率 p
で状態が完全混合状態に置き換わる)。もう一つの流儀は p を「何らかのパウリ
誤りが起きる確率」と読むもので、そちらは
pauli_depolarizing() です。両者は p_pauli = 3 * p / 4
のとき一致します。取り違えると全ての数値がずれるため、引数で切り替えるので
はなく別の名前にしてあります。
2量子ビットゲートの後、減極は既定ではそのゲートの両方の量子ビットに一括で
(4**k 個のパウリ積の混合として)作用します。 per_qubit=True を指定
すると、代わりに1量子ビットのチャネルを各量子ビットへ独立に当てます:
depolarizing(0.02) # cx の後に一括の2量子ビットチャネル
depolarizing(0.02, per_qubit=True) # 各量子ビットへ1量子ビットチャネル
両者は物理的に別の写像で、どちらにも用途があります。一括の方は k量子ビット チャネルを普通に書いたもので、純粋に局所的な雑音を仮定する論文が意味して いるのは独立の方です。同じ強度なら独立の方がやや強く減衰します(1つで済んで いたところに2つのチャネルが当たるためです)。
減衰チャネルは1量子ビットのチャネルで、ゲートが触れた各量子ビットに適用され ます。測定・リセット・バリアには雑音を載せません。
雑音モデル¶
チャネルを1つ渡すと全ゲートの後に適用されます。ゲートごとに誤り率を変えたい 場合(実機では2量子ビットゲートの誤りが一桁大きいのが普通です)は、ゲート名を 指定します:
from blueqat.noise import NoiseModel, depolarizing, amplitude_damping
nm = NoiseModel()
nm.add(depolarizing(0.001)) # 全ゲートの後
nm.add(depolarizing(0.01), gates=['cx', 'cz']) # さらにこれらの後
nm.add(amplitude_damping(0.002))
Circuit(3).h[0].cx[0, 1].run(noise=nm)
noise= はチャネルのリストも受け取ります(順に適用されます)。
準静的雑音¶
シリコンスピン量子ビットの主要な位相緩和は、マルコフ的なチャネルではありません。 核スピン(Overhauser)場や 1/f 電荷雑音は回路の実行よりはるかにゆっくり揺らぐ ため、1回の繰り返しの中では detuning がほぼ一定で、繰り返しにわたる平均が デコヒーレンスになります。この時間相関は Kraus 演算子では表現できません。 そして違いは学術的なものではなく、実測できます:
from blueqat.noise import QuasiStatic
Circuit(1).h[0].i[0].i[0].run(quasi_static=QuasiStatic(sigma=0.4),
samples=4000, seed=1)
各サンプルは量子ビットごとに1つの detuning を N(0, sigma) から引いて固定し、
回路の各層の後にすべての量子ビットへ rz(delta_q * dt) を積みます。得られた
密度行列を平均するので、これは detuning についての古典的な混合そのものです。
自由誘導減衰はガウス型 exp(-(sigma * t)**2 / 2) になります( t は層数)。
決定的な検証はハーンエコーです。待ち時間の中央での反転は、その前に溜まった 静的なずれを打ち消しますが、記憶を持たないチャネルには何の効果もありません。 同じ回路・同じ総待ち時間での実測コヒーレンス:
雑音 |
エコー無し |
エコー有り |
|---|---|---|
|
0.02 |
0.92 |
|
0.32 |
0.27 |
つまり T2* やエコーの実験を phase_damping() で再現しようと
すると、強度をどう調整しても答えは合いません。
samples は平均する detuning の本数です(既定200)。誤差は 1/sqrt(samples)
で減ります。準静的雑音はチャネルと併用できます( quasi_static= と noise=
の両方を渡してください)。
雑音の強度を変える¶
noise_scale=c は全チャネルの強度を c 倍します。ゼロ雑音外挿(ZNE)が
回すのがこのつまみです。同じ回路を複数の雑音強度で実行し、期待値をゼロへ
外挿します:
import numpy as np
from blueqat.utils import Z
c = Circuit(2).h[0].cx[0, 1]
h = 1.0 * Z[0] * Z[1]
scales = [1.0, 2.0, 3.0]
values = [float(c.run(noise=depolarizing(0.02), noise_scale=s, hamiltonian=h))
for s in scales]
zero_noise = np.polyfit(scales, values, 1)[-1] # 強度0へ外挿
強度パラメータが有効範囲を外れるスケールを指定した場合は、黙って丸めずに エラーになります(丸めた点は外挿を静かに壊すためです)。
noise_scale は準静的雑音にも効きますが、 sigma を c 倍ではなく
sqrt(c) 倍します。ガウス型の位相緩和は exp(-(sigma t)**2 / 2) で減衰する
ので、外挿が線形になるのは sigma**2 の側だからです。
雑音つき実行の結果¶
c.run(noise=nm) # 密度行列(2**n x 2**n のテンソル)
c.run(noise=nm, shots=1000, seed=1) # 対角成分からサンプリングしたcounts
c.run(noise=nm, hamiltonian=h) # Tr(rho H)
shots は状態ベクトルのバックエンドと同じ seed ・ bit_order を
受け取ります。 returns='statevector' ・ 'amplitude' ・ 'samples'
は状態ベクトルの概念なので、ここでは拒否されます。
計算量¶
密度行列は 4**n 個の要素を持ち、どのゲートも全要素に触れるため、この
バックエンドは小さい回路向けです。ゲートとその直後のチャネルは1枚の演算子に
掛け合わせて1回で適用しており(Kraus演算子を個別に当てるより約8倍速い)、
それでもスケーリングは変わりません:
量子ビット |
密度行列 |
1ゲートあたり |
|---|---|---|
8 |
1 MB |
0.4 ms |
10 |
17 MB |
9 ms |
12 |
268 MB |
183 ms |
10量子ビット程度までは快適、12まで実用的です。14を超えるとメモリを使い切る 前にバックエンドが拒否します。