前書き
量子ボリューム (QV) は、適度なサイズで近い将来に実現されるの量子コンピューター(near-term quantum computers)で具体的なプロトコルを使って測定できる単一の数値のメトリックです。QVメソッドでは、その量子コンピューターが正常に実装できる最大のランダムな量子回路の幅(width)と深さ(depth)を定量化します。量子コンピューティングシステムが、より高い量子ボリュームを持つためには、操作の忠実度が高いこと、量子ビットの連結数が多いこと、ゲートセットのキャリブレーションがより正確にされていること、回路書き換えのツールチェインを持つことが必要となってきます。
量子ボリュームのプロトコル
QVプロトコル([1]を参照)は、次のステップで構成されます:
(最初にデモに関連するqiskitのクラスをインポートする必要があります。)
import matplotlib.pyplot as plt
from IPython.display import clear_output
#Import Qiskit classes
import qiskit
from qiskit import assemble, transpile
from qiskit.providers.aer.noise import NoiseModel
from qiskit.providers.aer.noise.errors.standard_errors import depolarizing_error, thermal_relaxation_error
#QVの機能をインポート
import qiskit.ignis.verification.quantum_volume as qv
ステップ 1: QVシークエンスを生成する
量子アルゴリズムが、2量子ビットのユニタリーゲートで作られた多項式サイズの量子回路として表現できることはよく知られています。したがって、モデルとなる回路は、量子ビットの番号がランダムな並びとなる$d$個の層(layer)で構成され、その後にランダムな($SU(4)$による)2量子ビットゲートが続きます。回路の幅$m$が奇数の場合、各層で1つの量子ビットがアイドルになります。
より正確には、深さ$d$ 、 幅$m$ の QV回路 は、$d$個の層のシーケンス$U = U^{(d)}...U^{(2)}U^{(1)}$ です:
$$ U^{(t)} = U^{(t)}_{\pi_t(m'-1),\pi_t(m)} \otimes ... \otimes U^{(t)}_{\pi_t(1),\pi_t(2)} $$それぞれ時間が$t = 1 ... d$でラベルづけされ、$m' = 2 \lfloor n/2 \rfloor$の量子ビットに作用します。各層は、一様にランダムな順列$\pi_t \in S_m$($m$は$m$量子ビットを表す)を選択し、$SU(4)$のHaar測定から、量子ビット$a$および$b$に作用する各$U^{(t)}_{a,b}$をサンプリングすることによって指定されます。
次の例では、Q0、Q1、Q3、Q5、Q7、Q10の6つの量子ビットがあります。 フルセットまでのサブセットを調べます(各ボリューム回路の深さは、サブセットの量子ビットの数と同じです)。
# qubit_lists: QV回路を生成するための量子ビットサブセットのリストのリスト
qubit_lists = [[0,1,3],[0,1,3,5],[0,1,3,5,7],[0,1,3,5,7,10]]
# ntrials: サブセットごとに作成するランダム回路の数
ntrials = 100
量子ボリュームシーケンスを生成します。 (実行に時間がかかりすぎないように)小さな例から始めます。
import warnings
warnings.filterwarnings('ignore')
qv_circs, qv_circs_nomeas = qv.qv_circuits(qubit_lists, ntrials)
例として、最初のQVシーケンスに対応する回路を表示します。 理想的な回路は最初のn量子ビットで実行されることに注意してください(nはサブセット内の量子ビットの数です)。
#nomeasの最初の試行をtranspiler に通して回路を表示します
qv_circs_nomeas[0] = qiskit.compiler.transpile(qv_circs_nomeas[0], basis_gates=['u1','u2','u3','cx'])
qv_circs_nomeas[0][0].draw(fold=-1)
#ユニタリー行列はグローバルフェーズではidentity(単位行列)です
sv_sim = qiskit.Aer.get_backend('aer_simulator')
ideal_results = []
for trial in range(ntrials):
clear_output(wait=True)
for qc in qv_circs_nomeas[trial]:
qc.save_statevector()
result = qiskit.execute(qv_circs_nomeas[trial], backend=sv_sim).result()
ideal_results.append(result)
print(f'Simulated trial {trial+1}/{ntrials}')
次に、この理想的な結果を量子ボリューム・フィッターにロードします。
qv_fitter = qv.QVFitter(qubit_lists=qubit_lists)
qv_fitter.add_statevectors(ideal_results)
ステップ 3: ヘビー・アウトプットの計算をする
モデル回路$U$が実際に正常に実装された時を定義するには、ヘビー・アウトプット (heavy output)を生成する問題を使います。理想的な出力分布は$p_U(x) = |\langle x|U|0 \rangle|^2$です。ここで、$x \in \{0,1\}^m$は観測可能なビット文字列です。
昇順$p_0 \leq p_1 \leq \dots \leq p_{2^m-1}$でソートされた$p_U(x)$の範囲によって与えられる出力確率のセットを考えます。確率のセットの中央値は$p_{med} = (p_{2^{m-1}} + p_{2^{m-1}-1})/2$で、ヘビー・アウトプット は以下です。
$\hspace{15pt} H_U = \{ x \in \{0,1\}^m$ここで$ p_U(x)>p_{med} \}.$
重い出力生成する問題は、3分の2以上がヘビー・アウトプットの文字列のセットを生成することです。
例として、さまざまな深さからのヘビー・アウトプットとその確率を出力します(試行回数0の場合):
for qubit_list in qubit_lists:
l = len(qubit_list)
print ('qv_depth_'+str(l)+'_trial_0:', qv_fitter._heavy_outputs['qv_depth_'+str(l)+'_trial_0'])
for qubit_list in qubit_lists:
l = len(qubit_list)
print ('qv_depth_'+str(l)+'_trial_0:', qv_fitter._heavy_output_prob_ideal['qv_depth_'+str(l)+'_trial_0'])
noise_model = NoiseModel()
p1Q = 0.002
p2Q = 0.02
noise_model.add_all_qubit_quantum_error(depolarizing_error(p1Q, 1), 'u2')
noise_model.add_all_qubit_quantum_error(depolarizing_error(2*p1Q, 1), 'u3')
noise_model.add_all_qubit_quantum_error(depolarizing_error(p2Q, 2), 'cx')
Qiskit Aerシミュレーター(ノイズモデルあり)またはIBMQプロバイダーを使用してQVシーケンスを実行し、exp_resultsのリストを取得することができます。
aer_sim = qiskit.Aer.get_backend('aer_simulator')
basis_gates = ['u1','u2','u3','cx'] # use U,CX for now
shots = 1024
exp_results = []
for trial in range(ntrials):
clear_output(wait=True)
t_qcs = transpile(qv_circs[trial], basis_gates=basis_gates, optimization_level=3)
qobj = assemble(t_qcs)
result = aer_sim.run(qobj, noise_model=noise_model, max_parallel_experiments=0).result()
exp_results.append(result)
print(f'Completed trial {trial+1}/{ntrials}')
qv_fitter.add_data(exp_results)
for qubit_list in qubit_lists:
l = len(qubit_list)
#print (qv_fitter._heavy_output_counts)
print ('qv_depth_'+str(l)+'_trial_0:', qv_fitter._heavy_output_counts['qv_depth_'+str(l)+'_trial_0'])
plt.figure(figsize=(10, 6))
ax = plt.gca()
# plot_rb_dataを呼び出して重要なポイントをプロットします
qv_fitter.plot_qv_data(ax=ax, show_plt=False)
# タイトルとラベルを追加
ax.set_title('Quantum Volume for up to %d Qubits \n and %d Trials'%(len(qubit_lists[-1]), ntrials), fontsize=18)
plt.show()
それぞれの深さの統計をリストします。深さごとに、その深さに成功したかどうか、およびその信頼できる区間(confidence interval)をリストします。その深さが成功するためには、信頼できる区間が97.5%を超える必要があります。
qv_success_list = qv_fitter.qv_success()
qv_list = qv_fitter.ydata
QV = 1
for qidx, qubit_list in enumerate(qubit_lists):
if qv_list[0][qidx]>2/3:
if qv_success_list[qidx][0]:
print("Width/depth %d greater than 2/3 (%f) with confidence %f (successful). Quantum volume %d"%
(len(qubit_list),qv_list[0][qidx],qv_success_list[qidx][1],qv_fitter.quantum_volume()[qidx]))
QV = qv_fitter.quantum_volume()[qidx]
else:
print("Width/depth %d greater than 2/3 (%f) with confidence %f (unsuccessful)."%
(len(qubit_list),qv_list[0][qidx],qv_success_list[qidx][1]))
else:
print("Width/depth %d less than 2/3 (unsuccessful)."%len(qubit_list))
print ("The Quantum Volume is:", QV)
参考文献
[1] Andrew W. Cross, Lev S. Bishop, Sarah Sheldon, Paul D. Nation, and Jay M. Gambetta, Validating quantum computers using randomized model circuits, Phys. Rev. A 100, 032328 (2019). https://arxiv.org/pdf/1811.12926
import qiskit.tools.jupyter
%qiskit_version_table