ノイズありシミュレーション
実機上で動作する量子回路は、様々な確率的ノイズの影響を受けます。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: エラーを発生させることができるゲート。何も通過しない場合は、すべてのゲートがノイズの影響を受けていることを示します。
ここにエラーの一覧を示します:
名前 | 説明 | 入力 |
---|---|---|
BitFlipNoise | 1量子ビットのビット反転ノイズ | - error_prob - qubit_indices - target_gates |
PhaseFlipNoise | 1量子ビットの位相反転ノイズ | - error_prob - qubit_indices - target_gates |
BitPhaseFlipNoise | 1量子ビットのビット反転ノイズと位相反転ノイズ | - error_prob - qubit_indices - target_gates |
DepolarizingNoise | 1量子ビットの脱分極ノイズ | - error_prob - qubit_indices - target_gates |
PauliNoise
PauliNoise
はGateNoiseInstruction
のサブタイプで、複数の量子ビットにパウリゲートが作用します。以下にQURI Partsが提供するPauliNoise
を要約します。入力列では、すべてのノイズ命令がqubit_indices
とtarget_gates
を必要とするため、引数を省略していることに注意してください。また、状態にノイズが適用された後の密度行列の式も記載します。
名前 | 説明 | 入力 | ノイズ後の密度行列 |
---|---|---|---|
PauliNoise | 複数の量子ビットのパウリノイズ | - pauli_list - prob_list - eq_tolerance | |
GeneralDepolarizingNoise | 複数の量子ビット一般脱分極ノイズ | - error_prob - qubit_count |
ここで、GeneralDepolarizingNoise
では演算子はパウリ行列の積によって与えられることに注意してください:
また、第1項の和には、が含まれていないことに注意してください。
Kraus Noises
また、GateNoiseInstruction
のもう1つのサブタイプ、AbstractKrausNoise
も提供しています。これは、kraus_operators
プロパティを持つGateNoiseInstruction
で、ノイズを定義する明示的なクラウス演算子行列のリストを返します。ここでは、そのすべてをリストアップします。ここでもすべてのGateNoiseInstruction
がqubit_indices
とtarget_gates
の引数を必要とするため、入力列では引数を省略していることに注意してください。
名前 | 説明 | 入力 |
---|---|---|
KrausNoise | 複数量子ビットクラウスノイズ | - kraus_list: クラウス演算子行列のリスト |
ResetNoise | 1量子ビットのリセットノイズ | - p0: にリセットする確率 - p1: にリセットする確率 |
PhaseDampingNoise | 1量子ビットの位相減衰ノイズ | - phase_damping_rate |
AmplitudeDampingNoise | 1量子ビットの振幅減衰ノイズ | - amplitude_damping_rate - excited_state_population |
PhaseAmplitudeDampingNoise | 1量子ビットの位相と振幅減衰ノイズ | - phase_damping_rate - amplitude_damping_rate - excited_state_population |
ThermalRelaxationNoise | 1量子ビットの熱緩和ノイズ | - 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の中で、複数のGateNoiseInstructions
とCircuitNoiseInstrctions
を含むノイズモデルを表すオブジェクトです。また、QURI Partsでノイズ回路を作成する際に渡されるオブジェクトでもあります。NoiseModel
はNoiseInstructions
のシーケンスによって作成されます。Noiseは互いに交換しないので、NoiseInstruction
の順番が重要であることに注意してください。
また、モデルを修正するための便利なメソッドも用意されています。例えば、add_noise
やextend
を使ってモデルにノイズ命令を追加することができます:
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
期待通りの効果が出ています。