メインコンテンツまでスキップ

ノイズありシミュレーション

実機上で動作する量子回路は、様々な確率的ノイズの影響を受けます。QURI Partsでは、これらのノイズを表現し、シミュレータ上で再現するためのノイズモデルを定義することができます。(このチュートリアルではQulacsを使用します)。

前提条件

このチュートリアルで使用するQURI Partsモジュール: quri-parts-circuit, quri-parts-core,quri-parts-qulacs

以下のようにインストールすることができます:

!pip install "quri-parts[qulacs]"

概要

回路を準備

はじめに、ノイズを適用させる回路を準備します。

from quri_parts.circuit import QuantumCircuit
circuit = QuantumCircuit(3)
circuit.add_H_gate(2)
circuit.add_X_gate(0)
circuit.add_CNOT_gate(2, 1)
circuit.add_Z_gate(2)

ノイズモデルを作成

次に、ノイズモデルを作成します。ノイズとその適用条件を表すNoiseInstructionsを複数作成し、NoiseModelに追加します。

(これはAPIの機能を説明するためのノイズモデルであり、現実的な例ではありません。ノイズモデルは、対象となる実際の機器の特性に合わせて調整する必要があります。)

import quri_parts.circuit.gate_names as gate_names
from quri_parts.circuit.noise import (
BitFlipNoise,
BitPhaseFlipNoise,
DepolarizingNoise,
DepthIntervalNoise,
MeasurementNoise,
NoiseModel,
PauliNoise,
PhaseFlipNoise,
)
noises = [
# Single qubit noise
BitFlipNoise(
error_prob=0.004,
qubit_indices=[0, 2], # Qubit 0 or 2
target_gates=[gate_names.H, gate_names.CNOT], # H or CNOT gates
),
DepolarizingNoise(
error_prob=0.003,
qubit_indices=[], # All qubits
target_gates=[gate_names.X, gate_names.CNOT] # X or CNOT gates
),
PhaseFlipNoise(
error_prob=0.002,
qubit_indices=[1, 0], # Qubit 0 or 1
target_gates=[] # All kind of gates
),
BitPhaseFlipNoise(
error_prob=0.001,
qubit_indices=[], # All qubits
target_gates=[], # All kind of gates
),

# Multi qubit noise
PauliNoise(
pauli_list=[[1, 2], [2, 3]],
prob_list=[0.001, 0.002],
qubit_indices=[1, 2], # 2 qubit gates applying to qubits (1, 2) or (2, 1)
target_gates=[gate_names.CNOT] # CNOT gates
),

# Circuit noise
DepthIntervalNoise([PhaseFlipNoise(0.001)], depth_interval=5),
MeasurementNoise([BitFlipNoise(0.004), DepolarizingNoise(0.003)]),
]
model = NoiseModel(noises)

単一量子ビットノイズの場合、ターゲット量子ビットインデックスとターゲットゲート名を指定することができます。引数が省略されるか、空のリストが与えられると、全ての量子ビットまたはゲートがターゲットとして扱われます。

適用条件の指定方法はマルチ量子ビットノイズの場合も同様ですが、ターゲット量子ビットインデックスはターゲットゲートの量子ビットの完全なセット(順序は問いません)を必要とします。ノイズはターゲットゲートで指定された通りに並んだ量子ビットに適用されます。例えば、ターゲットゲートがCNOT(5, 3)と指定された場合、ノイズは(3, 5)ではなく、(5, 3)の量子ビットに適用されます。

インターフェース

このセクションでは、NoiseInstructionインターフェースと、QURI Partsが提供するさまざまなNoiseInstructionを紹介します。NoiseInstructionは2種類のノイズ命令を表す型のエイリアスです:

  • CircuitNoiseInstruction:回路の構造に応じて適用されるノイズを表す
  • GateNoiseInstruction:個々のゲートが量子ビットに作用するときに適用されるノイズを表す

CircuitNoiseInstruction

CircuitNoiseInstructionは、回路の構造に応じて適用されるノイズを表します。QURI Partsが提供するCircuitNoiseInstructionの一覧です。

名前説明入力
GateIntervalNoiseそれぞれの量子ビットに対して、あるゲート数が適用されるたびに、与えられた1量子ビットのノイズが適用される- single_qubit_noises
- gate_interval
DepthIntervalNoiseあるdepthだけ進むたびに、与えられた単一量子ビットのGateNoiseInstructionをすべての量子ビットに適用します- single_qubit_noises
- depth_interval
MeasurementNoise量子ビットの測定中に発生するノイズ- single_qubit_noises
- qubit_indices

GateNoiseInstruction

GateNoiseInstructionは、個々のゲートが量子ビットに作用するときに適用されるノイズを表します。以下の属性を持つデータクラスです:

  • name: ノイズの名前
  • qubit_count: このエラーが影響する量子ビットの数
  • params: エラー確率などのパラメータ(具体的なエラータイプによる)
  • qubit_indices: このエラーが影響する量子ビットのインデックス
  • target_gates: このエラーの影響を受けるゲート

QURI Partsは、GateNoiseInstructionsのいくつかの実装と、それらのいくつかの特殊なサブタイプを提供します:

基本的なGateNoiseInstruction

ここではまず、GateNoiseInstructionの基本的な実装から始めます。これらは3つのパラメータで構成されます:

  • error_prob: エラーが発生する確率
  • qubit_indices: エラーが発生する可能性のある量子ビット。何も通過しない場合は、回路内のすべての量子ビットでエラーが発生する可能性があることを示します。
  • target_gates: エラーを発生させることができるゲート。何も通過しない場合は、すべてのゲートがノイズの影響を受けていることを示します。

ここにエラーの一覧を示します:

名前説明入力
BitFlipNoise1量子ビットのビット反転ノイズ- error_prob
- qubit_indices
- target_gates
PhaseFlipNoise1量子ビットの位相反転ノイズ- error_prob
- qubit_indices
- target_gates
BitPhaseFlipNoise1量子ビットのビット反転ノイズと位相反転ノイズ- error_prob
- qubit_indices
- target_gates
DepolarizingNoise1量子ビットの脱分極ノイズ- error_prob
- qubit_indices
- target_gates

PauliNoise

PauliNoiseGateNoiseInstructionのサブタイプで、複数の量子ビットにパウリゲートが作用します。以下にQURI Partsが提供するPauliNoiseを要約します。入力列では、すべてのノイズ命令がqubit_indicestarget_gatesを必要とするため、引数を省略していることに注意してください。また、状態にノイズが適用された後の密度行列の式も記載します。

名前説明入力ノイズ後の密度行列
PauliNoise複数の量子ビットのパウリノイズ- pauli_list
- prob_list
- eq_tolerance
ipiPiρPi+(1ipi)ρ\sum_{i}p_i P_{i} \rho P_{i} + (1-\sum_{i}p_i)\rho
GeneralDepolarizingNoise複数の量子ビット一般脱分極ノイズ- error_prob
- qubit_count
p4n1i1=03in=03Ei1inρEi1in+(1p)ρ\frac{p}{4^n - 1} \sum_{i_1=0}^3\cdots\sum_{i_n=0}^3 E_{i_1 \cdots i_n} \rho E_{i_1 \cdots i_n} + (1-p)\rho

ここで、GeneralDepolarizingNoiseでは演算子Ei1inE_{i_1\cdots i_n}はパウリ行列の積によって与えられることに注意してください:

Eiiin=Xi1Xin\begin{equation} E_{i_i\cdots i_n} = X_{i_1} \cdots X_{i_n} \end{equation}

また、第1項の和には、{i1,,in}={0,,0}\{i_1, \cdots, i_n \} = \{0, \cdots, 0\}が含まれていないことに注意してください。

Kraus Noises

また、GateNoiseInstructionのもう1つのサブタイプ、AbstractKrausNoiseも提供しています。これは、kraus_operatorsプロパティを持つGateNoiseInstructionで、ノイズを定義する明示的なクラウス演算子行列のリストを返します。ここでは、そのすべてをリストアップします。ここでもすべてのGateNoiseInstructionqubit_indicestarget_gatesの引数を必要とするため、入力列では引数を省略していることに注意してください。

名前説明入力
KrausNoise複数量子ビットクラウスノイズ- kraus_list: クラウス演算子行列のリスト
ResetNoise1量子ビットのリセットノイズ- p0: 0\|0\rangleにリセットする確率
- p1: 1\|1\rangleにリセットする確率
PhaseDampingNoise1量子ビットの位相減衰ノイズ- phase_damping_rate
AmplitudeDampingNoise1量子ビットの振幅減衰ノイズ- amplitude_damping_rate
- excited_state_population
PhaseAmplitudeDampingNoise1量子ビットの位相と振幅減衰ノイズ- phase_damping_rate
- amplitude_damping_rate
- excited_state_population
ThermalRelaxationNoise1量子ビットの熱緩和ノイズ- t1
- t2
- gate_time
- excited_state_population

例えば、リセットノイズを見てみましょう:

from quri_parts.circuit.noise import ResetNoise

reset_noise = ResetNoise(p0=0.04, p1=0.16)
reset_noise.kraus_operators
#output
(array([[0.89442719, 0. ],
[0. , 0.89442719]]),
array([[0.2, 0. ],
[0. , 0. ]]),
array([[0. , 0.2],
[0. , 0. ]]),
array([[0. , 0. ],
[0.4, 0. ]]),
array([[0. , 0. ],
[0. , 0.4]]))

NoiseModel

最後に、NoiseModelオブジェクトを紹介します。これはQURI Partsの中で、複数のGateNoiseInstructionsCircuitNoiseInstrctionsを含むノイズモデルを表すオブジェクトです。また、QURI Partsでノイズ回路を作成する際に渡されるオブジェクトでもあります。NoiseModelNoiseInstructionsのシーケンスによって作成されます。Noiseは互いに交換しないので、NoiseInstructionの順番が重要であることに注意してください。

また、モデルを修正するための便利なメソッドも用意されています。例えば、add_noiseextendを使ってモデルにノイズ命令を追加することができます:

model = NoiseModel([
BitFlipNoise(
error_prob=0.004,
qubit_indices=[0, 2],
target_gates=[gate_names.H, gate_names.CNOT],
),
DepolarizingNoise(
error_prob=0.003,
target_gates=[gate_names.X, gate_names.CNOT]
),
])

# Add a single instruction
model.add_noise(PhaseFlipNoise(error_prob=0.002, qubit_indices=[1, 0]))
model.add_noise(BitPhaseFlipNoise(error_prob=0.001,))

# Add a sequence of instructions
model.extend([
PauliNoise(
pauli_list=[[1, 2], [2, 3]],
prob_list=[0.001, 0.002],
qubit_indices=[1, 2],
target_gates=[gate_names.CNOT]
),
DepthIntervalNoise([PhaseFlipNoise(0.001)], depth_interval=5),
MeasurementNoise([BitFlipNoise(0.004), DepolarizingNoise(0.003)])
])

また、モデル内のCircuitNoiseInstrcutionsを検査するために、.noises_for_circuitも提供しています。.noises_for_gateは、特定のゲートのゲートノイズを特定します。

from quri_parts.circuit import X

print("Circuit noises:", model.noises_for_circuit())

print("")
print("Gate noise on X(0):")
for noise in model.noises_for_gate(X(0)):
print(noise)
#output
Circuit noises: [DepthIntervalNoise(name=DepthIntervalNoise), MeasurementNoise(name=MeasurementNoise)]

Gate noise on X(0):
((0,), DepolarizingNoise(name='DepolarizingNoise', qubit_count=1, params=(0.003,), qubit_indices=(), target_gates=('X', 'CNOT')))
((0,), PhaseFlipNoise(name='PhaseFlipNoise', qubit_count=1, params=(0.002,), qubit_indices=(1, 0), target_gates=()))
((0,), BitPhaseFlipNoise(name='BitPhaseFlipNoise', qubit_count=1, params=(0.001,), qubit_indices=(), target_gates=()))

Qulacsでノイズありの系のシミュレーションをする

ノイズモデルを含む回路の変換

ノイズモデルを直接適用したQulacs回路が必要な場合は、以下の回路変換機能が提供されています。サンプリングや期待値推定などの目的では、通常、ユーザーがこの変換を行う必要はありません。ただし、Qulacsを直接使用する場合は、Qulacsチュートリアルのノイズありシミュレーションを参照してください。

from quri_parts.qulacs.circuit.noise import convert_circuit_with_noise_model
qulacs_circuit = convert_circuit_with_noise_model(circuit, model)

Qulacsによるサンプリング・シミュレーション

サンプリングに関しては、ノイズモデルを適用したサンプラーを作成するための関数がいくつか用意されています。

from quri_parts.qulacs.sampler import create_qulacs_density_matrix_sampler
density_matrix_sampler = create_qulacs_density_matrix_sampler(model)
counts = density_matrix_sampler(circuit, shots=1000)
counts
#output
Counter({1: 486, 7: 490, 0: 9, 5: 8, 3: 5, 4: 1, 6: 1})
from quri_parts.qulacs.sampler import create_qulacs_stochastic_state_vector_sampler
stochastic_state_vector_sampler = create_qulacs_stochastic_state_vector_sampler(model)
counts = stochastic_state_vector_sampler(circuit, shots=1000)
counts
#output
Counter({1: 478, 7: 489, 5: 19, 3: 9, 6: 1, 0: 4})

密度行列サンプラー(create_qulacs_density_matrix_sampler()で作成)は密度行列を用いて測定確率を計算しサンプリングを行いますが、確率的状態ベクトルサンプラー(create_qulacs_stochastic_state_vector_sampler()で作成)は指定したショット数に対して確率的状態ベクトルシミュレーションを繰り返すことでサンプリングを行います。計算時間は回路の種類によって異なりますが、一般にショット数が10^3程度以下の場合は確率的状態ベクトルサンプラーが有利です。

ノイズモデル付きSamplerの使い方は、作成時にNoiseModelを指定する以外は、他のSamplerと同じです。通常のSamplerと同様、コンカレント実行に対応したバージョンもあります。Samplerの使い方はAPIドキュメントやSamplerチュートリアルを参照してください。

Qulacsによる演算子の期待値の推定

また、推定では、ノイズモデルを適用したEstimatorを作成することができます。

from quri_parts.qulacs.estimator import create_qulacs_density_matrix_estimator
density_matrix_estimator = create_qulacs_density_matrix_estimator(model)

Samplerの場合と同様、ノイズモデル付きEstimatorの使い方は他のEstimatorと同じですが、作成時にNoiseModelを指定する必要があります。通常のEstimatorと同様に、パラメトリック回路やコンカレント実行をサポートするバージョンもあります。Estimatorの使い方はAPIドキュメントやEstimatorチュートリアルを参照してください。

最後に、ノイズモデルが機能していることを確認するために、簡単な例を挙げてみましょう。Xゲートを1つだけ適用した回路を作成し、空のノイズモデルで期待値を計算します。

from quri_parts.core.operator import pauli_label
from quri_parts.core.state import quantum_state

circuit = QuantumCircuit(1)
circuit.add_X_gate(0)
state = quantum_state(1, circuit=circuit)

pauli = pauli_label("Z0")

empty_model = NoiseModel()

estimator = create_qulacs_density_matrix_estimator(empty_model)
estimate = estimator(pauli, state)
estimate.value
#output
(-1+0j)

結果は予想通りです。次に、確率0.5のフリップノイズをノイズモデルに追加してみましょう。

bitflip_model = NoiseModel([BitFlipNoise(0.5)])

noised_estimator = create_qulacs_density_matrix_estimator(bitflip_model)
noised_estimate = noised_estimator(pauli, state)
noised_estimate.value
#output
0j

期待通りの効果が出ています。