Skip to content

噪声模拟

真实量子硬件存在噪声。本教程介绍如何使用 NoiseModel 框架和七种内置量子误差通道,在 pyqpanda3 中建模和模拟噪声。


为什么噪声模拟很重要

量子计算机运行在 NISQ(含噪中等规模量子)时代,硬件受到各种噪声源的影响:

  • 门误差:不完美的门实现
  • 读出误差:测量错误地报告结果
  • 退相干:量子比特随时间丢失量子信息(T1 弛豫、T2 退相位)

噪声模拟可以帮助你:

  1. 预测算法在真实硬件上的表现
  2. 开发抗噪声算法
  3. 表征和校准量子设备
  4. 验证纠错方案

噪声模型框架

NoiseModel 类

NoiseModel 类将量子误差和读出误差收集到一个统一的模型中,可以传递给任何模拟器:

python
from pyqpanda3 import core

# Create an empty noise model
noise = core.NoiseModel()

# Check if any noise is configured
print(f"Noise enabled: {noise.is_enabled()}")
# Output: Noise enabled: False

添加门误差

为特定的门和量子比特添加量子误差:

python
from pyqpanda3 import core

# Add error to a specific gate type on specific qubits
error = core.depolarizing_error(0.01)
noise.add_quantum_error(error, core.GateType.CNOT, [0, 1])

# Add error to multiple gate types on specific qubits
noise.add_quantum_error(error, [core.GateType.H, core.GateType.T], [0])

# Add error to a gate type on ALL qubits
noise.add_all_qubit_quantum_error(error, core.GateType.CNOT)

# Add error to multiple gate types on ALL qubits
noise.add_all_qubit_quantum_error(error, [core.GateType.H, core.GateType.CNOT])

添加读出误差

读出误差建模测量到错误经典值的概率:

python
from pyqpanda3 import core

# Add readout error to a specific qubit
# probs[i][j] = P(measuring j | true state is i)
noise.add_read_out_error([[0.95, 0.05], [0.05, 0.95]], 0)

# Add readout error to ALL qubits
noise.add_all_qubit_read_out_error([[0.95, 0.05], [0.05, 0.95]])

量子误差通道

pyqpanda3 提供七种量子误差通道,每种建模不同的物理噪声机制。所有误差通道均使用 Kraus 算子描述。

Kraus 表示

每个量子噪声通道都可以用一组 Kraus 算子 {Ki} 来描述:

E(ρ)=iKiρKi

其中 iKiKi=I(完备性关系)。

1. Pauli X 误差(比特翻转)

比特翻转通道以概率 p 随机应用 X(NOT)门:

E(ρ)=(1p)ρ+pXρX

Kraus 算子:

  • K0=1pI
  • K1=pX
python
from pyqpanda3 import core

# Create a bit flip error with probability 0.05
error_x = core.pauli_x_error(0.05)

物理模型:能量弛豫导致 |0|1 翻转

2. Pauli Y 误差

Pauli Y 误差以概率 p 应用 Y 门:

E(ρ)=(1p)ρ+pYρY

Kraus 算子:

  • K0=1pI
  • K1=pY
python
from pyqpanda3 import core

# Create a Pauli Y error with probability 0.03
error_y = core.pauli_y_error(0.03)

3. Pauli Z 误差(相位翻转)

相位翻转通道以概率 p 应用 Z 门:

E(ρ)=(1p)ρ+pZρZ

Kraus 算子:

  • K0=1pI
  • K1=pZ
python
from pyqpanda3 import core

# Create a phase flip error with probability 0.02
error_z = core.pauli_z_error(0.02)

物理模型:退相位在 |0|1 之间引入相对相位

4. 去极化误差

去极化通道以概率 p 将态替换为最大混合态 I/d

E(ρ)=(1p)ρ+pId

对于单量子比特(d=2),这等价于以等概率应用每个 Pauli 算子:

E(ρ)=(1p)ρ+p3(XρX+YρY+ZρZ)

Kraus 算子:

  • K0=1pI
  • K1=p/3X
  • K2=p/3Y
  • K3=p/3Z
python
from pyqpanda3 import core

# Create a depolarizing error with probability 0.01
error_dep = core.depolarizing_error(0.01)

物理模型:将态驱向最大熵的通用噪声

5. 振幅阻尼误差

振幅阻尼通道建模 T1 弛豫(能量耗散)。它描述了 |1|0 的衰减:

E(ρ)=K0ρK0+K1ρK1

Kraus 算子:

  • K0=(1001γ)
  • K1=(0γ00)
python
from pyqpanda3 import core

# Create an amplitude damping error with decay rate 0.02
error_ad = core.amplitude_damping_error(0.02)

物理模型:T1 弛豫——量子比特向环境损失能量

6. 相位阻尼误差

相位阻尼通道建模 T2 退相位(相位相干性的丧失):

E(ρ)=K0ρK0+K1ρK1

Kraus 算子:

  • K0=(1001λ)
  • K1=(000λ)
python
from pyqpanda3 import core

# Create a phase damping error with rate 0.01
error_pd = core.phase_damping_error(0.01)

物理模型:T2 退相位——无能量损失情况下量子相位信息的丢失

7. 退相干误差

退相干误差结合了振幅阻尼和相位阻尼,同时建模 T1 和 T2 过程:

python
from pyqpanda3 import core

# Create a decoherence error (T1=80us, T2=60us, gate_time=20ns)
error_dec = core.decoherence_error(80.0, 60.0, 20.0)

物理模型:T1 和 T2 退相干过程的综合

误差通道汇总

误差物理模型Kraus 算子参数
pauli_x_error(p)比特翻转K0=1pI, K1=pXp:翻转概率
pauli_y_error(p)比特+相位翻转K0=1pI, K1=pYp:误差概率
pauli_z_error(p)相位翻转K0=1pI, K1=pZp:翻转概率
depolarizing_error(p)通用噪声4 个算子p:去极化概率
amplitude_damping_error(γ)T1 衰减2 个算子γ:阻尼率
phase_damping_error(λ)T2 退相位2 个算子λ:退相位率
decoherence_error(T1, T2, t_gate)T1+T2 综合多个算子T1、T2(μs)、t_gate(ns)

QuantumError 类

QuantumError 类封装噪声数据并提供组合操作:

创建量子误差

python
from pyqpanda3 import core
import numpy as np

# Method 1: From Kraus matrices (list of numpy arrays)
kraus = [
    np.sqrt(0.99) * np.eye(2),
    np.sqrt(0.01) * np.array([[0, 1], [1, 0]])
]
error = core.QuantumError(kraus)

# Method 2: From noise map (Pauli probabilities)
noise_map = {"X": 0.01, "Y": 0.005, "Z": 0.005}
error2 = core.QuantumError(noise_map)

误差属性

python
from pyqpanda3 import core

# Get number of qubits affected
print(f"Qubits affected: {error.qubit_num()}")

# Get the error type
print(f"Error type: {error.error_type()}")

# Get Kraus matrices
karus = error.error_karus()
print(f"Kraus matrices: {karus}")

误差组合

python
from pyqpanda3 import core

# Tensor product of two errors (independent errors on different qubits)
combined = error.tensor(error2)

# Expansion to a larger system
expanded = error.expand(error2)

# Sequential composition (error applied after another)
composed = error.compose(error2)

比较噪声通道

不同的噪声通道以根本不同的方式影响量子态。理解这些差异对于为你的用例选择正确的误差模型至关重要。

误差效果的可视化比较

通道比较表

下表在关键特性上比较了所有七种误差通道:

通道保持能量?保持相位?CPTP?幺正?典型物理来源
Pauli X自旋弛豫
Pauli Y比特+相位翻转的组合
Pauli Z磁通量噪声
Depolarizing通用门误差
Amplitude Damping否(不可逆)T1 能量衰减
Phase DampingT2 纯退相位
DecoherenceT1+T2 综合

如果一个通道将恒等映射到恒等,即 E(I)=I,则称其为幺正的(unital)。幺正通道将态驱向最大混合态 I/d

代码示例:比较所有误差类型

以下代码将七种误差通道分别应用于同一个 Bell 态电路,并测量输出分布相对于理想结果的变化。

python
from pyqpanda3 import core
import math

# Define a helper to build a Bell state circuit
def make_bell_circuit():
    prog = core.QProg()
    prog << core.H(0) << core.CNOT(0, 1)
    prog << core.measure([0, 1], [0, 1])
    return prog

# Error probability or rate to use for each channel
error_param = 0.05

# Map each channel name to its creation function and parameter
error_builders = {
    "Pauli X":  lambda: core.pauli_x_error(error_param),
    "Pauli Y":  lambda: core.pauli_y_error(error_param),
    "Pauli Z":  lambda: core.pauli_z_error(error_param),
    "Depolarizing": lambda: core.depolarizing_error(error_param),
    "Amp. Damping": lambda: core.amplitude_damping_error(error_param),
    "Phase Damping": lambda: core.phase_damping_error(error_param),
    "Decoherence": lambda: core.decoherence_error(80.0, 60.0, 20.0),
}

# Run each error model and record the Bell-state fidelity
prog = make_bell_circuit()
shots = 10000

for name, builder in error_builders.items():
    noise = core.NoiseModel()
    err = builder()
    # Apply the error to every gate on all qubits
    noise.add_all_qubit_quantum_error(err, core.GateType.CNOT)
    noise.add_all_qubit_quantum_error(err, core.GateType.H)

    machine = core.CPUQVM()
    machine.run(prog, shots=shots, model=noise)
    counts = machine.result().get_counts()

    # Compute fidelity as fraction of results in {|00>, |11>}
    bell = counts.get("00", 0) + counts.get("11", 0)
    fidelity = bell / shots
    print(f"{name:18s}  fidelity={fidelity:.4f}  counts={counts}")

预期输出模式(由于采样,数值会有所不同):

Pauli X             fidelity=0.8950  counts={'00': 4475, '11': 4475, '01': 525, '10': 525}
Pauli Y             fidelity=0.8950  counts={'00': 4480, '11': 4470, '01': 520, '10': 530}
Pauli Z             fidelity=0.9500  counts={'00': 4750, '11': 4750, '01': 250, '10': 250}
Depolarizing        fidelity=0.8800  counts={'00': 4400, '11': 4400, '01': 600, '10': 600}
Amp. Damping        fidelity=0.9200  counts={'00': 5000, '11': 4200, '01': 400, '10': 400}
Phase Damping       fidelity=0.9500  counts={'00': 4750, '11': 4750, '01': 250, '10': 250}
Decoherence         fidelity=0.8700  counts={'00': 4800, '11': 3900, '01': 650, '10': 650}

从比较中可以得出以下关键观察:

  • Pauli X 和 Y 误差产生对称的 Bell 基泄漏,在 |01|10 中给出大致相等的布居数。
  • Pauli Z 误差不改变计算基上的测量结果,因此在此特定电路上似乎保持了 Bell 保真度。然而,它们会破坏相位相干性。
  • 去极化噪声在单位概率下最具破坏性,因为它同时包含所有三个 Pauli 分量。
  • 振幅阻尼是不对称的:它优先将 |1 驱向 |0,因此 |00 的计数多于 |11

误差传播分析

当电路有多层门时,误差会累积。理解噪声如何随电路深度扩展对于预测算法性能至关重要。

理论背景

对于具有 d 层(深度)的电路,每层每个门有独立的去极化误差率 p,总误差概率近似增长为:

ptotal1(1p)ngates

其中 ngates 是门的总数。对于较小的 p,这简化为:

ptotalngatesp

输出保真度(与理想态的重叠)遵循指数衰减:

F(d)F0eαd

其中 α 是取决于每层误差率的衰减常数。

测量保真度与电路深度

以下实验通过运行深度递增的电路并测量输出保真度来展示噪声的累积过程。

python
from pyqpanda3 import core
import math

def build_layer_circuit(num_qubits, num_layers):
    """Build a circuit with repeated layers of H + CNOT gates."""
    prog = core.QProg()
    for layer in range(num_layers):
        # Apply Hadamard to every qubit
        for q in range(num_qubits):
            prog << core.H(q)
        # Apply CNOT between adjacent qubits
        for q in range(0, num_qubits - 1, 2):
            prog << core.CNOT(q, q + 1)
    prog << core.measure(list(range(num_qubits)), list(range(num_qubits)))
    return prog

# Set up a moderate noise model
noise = core.NoiseModel()
noise.add_all_qubit_quantum_error(
    core.depolarizing_error(0.005), core.GateType.H
)
noise.add_all_qubit_quantum_error(
    core.depolarizing_error(0.02), core.GateType.CNOT
)

# Measure fidelity as a function of depth
num_qubits = 4
shots = 5000

for depth in [1, 2, 4, 8, 16, 32]:
    prog = build_layer_circuit(num_qubits, depth)

    # Run with noise
    machine = core.CPUQVM()
    machine.run(prog, shots=shots, model=noise)
    noisy_counts = machine.result().get_counts()

    # Run without noise for comparison
    machine.run(prog, shots=shots)
    ideal_counts = machine.result().get_counts()

    # Compute total variation distance as a noise metric
    all_keys = set(list(ideal_counts.keys()) + list(noisy_counts.keys()))
    tvd = 0.0
    for key in all_keys:
        p_ideal = ideal_counts.get(key, 0) / shots
        p_noisy = noisy_counts.get(key, 0) / shots
        tvd += abs(p_ideal - p_noisy)
    tvd /= 2.0

    print(f"Depth={depth:3d}  TVD={tvd:.4f}  "
          f"ideal_keys={len(ideal_counts)}  noisy_keys={len(noisy_counts)}")

每种门类型的误差累积

并非所有门都以相同的速率累积误差。双量子比特门的误差率通常是单量子比特门的 10-100 倍:

Ftotali=1N1q(1p1q)j=1N2q(1p2q)

其中 N1qN2q 分别是单量子比特门和双量子比特门的数量。

python
from pyqpanda3 import core

# Demonstrate that two-qubit gate errors dominate
prog = core.QProg()
prog << core.H(0) << core.H(1) << core.H(2)
prog << core.CNOT(0, 1) << core.CNOT(1, 2)
prog << core.measure([0, 1, 2], [0, 1, 2])

# Noise only on single-qubit gates
noise_1q = core.NoiseModel()
noise_1q.add_all_qubit_quantum_error(
    core.depolarizing_error(0.005), core.GateType.H
)

# Noise only on two-qubit gates
noise_2q = core.NoiseModel()
noise_2q.add_all_qubit_quantum_error(
    core.depolarizing_error(0.02), core.GateType.CNOT
)

shots = 10000
machine = core.CPUQVM()

# Ideal baseline
machine.run(prog, shots=shots)
ideal = machine.result().get_counts()

# Single-qubit noise only
machine.run(prog, shots=shots, model=noise_1q)
counts_1q = machine.result().get_counts()

# Two-qubit noise only
machine.run(prog, shots=shots, model=noise_2q)
counts_2q = machine.result().get_counts()

print(f"Ideal:      {ideal}")
print(f"1q noise:   {counts_1q}")
print(f"2q noise:   {counts_2q}")
# 2q noise will produce noticeably more deviation from the ideal distribution

GateType 枚举

向噪声模型添加误差时,使用 GateType 枚举指定门类型:

python
# Single-qubit gates
core.GateType.H       # Hadamard
core.GateType.X       # Pauli-X
core.GateType.Y       # Pauli-Y
core.GateType.Z       # Pauli-Z
core.GateType.S       # Phase gate
core.GateType.T       # T gate
core.GateType.RX      # RX rotation
core.GateType.RY      # RY rotation
core.GateType.RZ      # RZ rotation
core.GateType.U1      # U1 gate
core.GateType.U2      # U2 gate
core.GateType.U3      # U3 gate
core.GateType.U4      # U4 gate

# Two-qubit gates
core.GateType.CNOT    # Controlled-NOT
core.GateType.CZ      # Controlled-Z
core.GateType.CP      # Controlled-Phase
core.GateType.CRX     # Controlled-RX
core.GateType.CRY     # Controlled-RY
core.GateType.CRZ     # Controlled-RZ
core.GateType.SWAP    # SWAP
core.GateType.ISWAP   # iSWAP
core.GateType.SQISWAP # Square-root iSWAP
core.GateType.RXX     # RXX rotation
core.GateType.RYY     # RYY rotation
core.GateType.RZZ     # RZZ rotation
core.GateType.RZX     # RZX rotation

# Multi-qubit gates
core.GateType.TOFFOLI # Toffoli (CCNOT)

运行噪声模拟

使用 CPUQVM

python
from pyqpanda3 import core

# Build circuit
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1)
prog << core.measure([0, 1], [0, 1])

# Create noise model
noise = core.NoiseModel()
noise.add_all_qubit_quantum_error(core.depolarizing_error(0.01), core.GateType.CNOT)
noise.add_all_qubit_read_out_error([[0.95, 0.05], [0.05, 0.95]])

# Run with noise
machine = core.CPUQVM()
machine.run(prog, shots=10000, model=noise)
print(machine.result().get_counts())

使用 DensityMatrixSimulator

python
from pyqpanda3 import core

# Build circuit (no measurement needed)
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1)

# Create noise model
noise = core.NoiseModel()
noise.add_all_qubit_quantum_error(
    core.amplitude_damping_error(0.05),
    core.GateType.H
)
noise.add_all_qubit_quantum_error(
    core.depolarizing_error(0.02),
    core.GateType.CNOT
)

# Run with exact noise simulation
dm_sim = core.DensityMatrixSimulator()
dm_sim.run(prog, noise)

# Get noisy state probabilities
print(f"Probabilities: {dm_sim.state_probs()}")
print(f"Density matrix:\n{dm_sim.density_matrix()}")

预期输出:

Probabilities: {0: 0.475, 1: 0.025, 2: 0.025, 3: 0.475}
Density matrix:
[[0.475  0.   0.    0.475]
 [0.     0.025 0.025 0.   ]
 [0.     0.025 0.025 0.   ]
 [0.475  0.   0.    0.475]]

使用 Stabilizer

python
from pyqpanda3 import core

# Clifford circuit
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1)
prog << core.measure([0, 1], [0, 1])

# Noise model (only Pauli errors for Stabilizer)
noise = core.NoiseModel()
noise.add_all_qubit_quantum_error(
    core.pauli_x_error(0.01),
    core.GateType.H
)
noise.add_all_qubit_quantum_error(
    core.depolarizing_error(0.02),
    core.GateType.CNOT
)

# Run noisy Stabilizer simulation
stab = core.Stabilizer()
stab.run(prog, shots=10000, model=noise)
print(stab.result().get_counts())

实用示例

示例 1:建模真实量子设备

python
from pyqpanda3 import core

def create_device_noise_model():
    """Create a noise model approximating a real superconducting device."""
    noise = core.NoiseModel()

    # Single-qubit gate errors (higher fidelity)
    single_gate_error = core.depolarizing_error(0.001)
    noise.add_all_qubit_quantum_error(single_gate_error, [
        core.GateType.H,
        core.GateType.X,
        core.GateType.Y,
        core.GateType.Z,
        core.GateType.S,
        core.GateType.T,
    ])

    # Two-qubit gate errors (lower fidelity)
    two_gate_error = core.depolarizing_error(0.01)
    noise.add_all_qubit_quantum_error(two_gate_error, [
        core.GateType.CNOT,
        core.GateType.CZ,
    ])

    # T1 decay
    t1_error = core.amplitude_damping_error(0.005)
    noise.add_all_qubit_quantum_error(t1_error, [
        core.GateType.H,
        core.GateType.X,
    ])

    # Readout errors
    noise.add_all_qubit_read_out_error([
        [0.97, 0.03],  # P(measure 0 | true 0), P(measure 1 | true 0)
        [0.04, 0.96]   # P(measure 0 | true 1), P(measure 1 | true 1)
    ])

    return noise

# Use the realistic noise model
noise = create_device_noise_model()

prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1) << core.measure([0, 1], [0, 1])

machine = core.CPUQVM()
machine.run(prog, shots=10000, model=noise)
counts = machine.result().get_counts()
print(f"Device-like results: {counts}")
# With noise, will see small counts in |01⟩ and |10⟩

示例 2:比较噪声水平

python
from pyqpanda3 import core

def run_with_noise_level(prob, shots=5000):
    """Run a Bell state with different depolarizing noise levels."""
    noise = core.NoiseModel()
    noise.add_all_qubit_quantum_error(
        core.depolarizing_error(prob),
        core.GateType.CNOT
    )

    prog = core.QProg()
    prog << core.H(0) << core.CNOT(0, 1)
    prog << core.measure([0, 1], [0, 1])

    machine = core.CPUQVM()
    machine.run(prog, shots=shots, model=noise)
    return machine.result().get_counts()

# Compare noise levels
for p in [0.0, 0.01, 0.05, 0.1, 0.2]:
    counts = run_with_noise_level(p)
    bell_prob = (counts.get('00', 0) + counts.get('11', 0)) / 5000
    print(f"p={p:.2f}: Bell fidelity ≈ {bell_prob:.3f}, counts={counts}")

示例 3:逐量子比特噪声配置

python
from pyqpanda3 import core

# Different noise levels for different qubits
noise = core.NoiseModel()

# Qubit 0 has higher error rate
noise.add_quantum_error(
    core.depolarizing_error(0.02), core.GateType.H, [0]
)
noise.add_quantum_error(
    core.depolarizing_error(0.05), core.GateType.CNOT, [0, 1]
)

# Qubit 2 has lower error rate
noise.add_quantum_error(
    core.depolarizing_error(0.005), core.GateType.H, [2]
)
noise.add_quantum_error(
    core.depolarizing_error(0.01), core.GateType.CNOT, [2, 3]
)

# Run a 4-qubit circuit with per-qubit noise
prog = core.QProg()
prog << core.H(0) << core.H(2)
prog << core.CNOT(0, 1) << core.CNOT(2, 3)
prog << core.measure([0, 1, 2, 3], [0, 1, 2, 3])

machine = core.CPUQVM()
machine.run(prog, shots=10000, model=noise)
print(machine.result().get_counts())

示例 4:噪声下的 GHZ 态

Greenberger-Horne-Zeilinger(GHZ)态 12(|000+|111) 对噪声高度敏感,因为其量子关联跨越所有量子比特。本示例测量在真实噪声模型下 GHZ 保真度如何随量子比特数退化。

python
from pyqpanda3 import core

def build_ghz_circuit(num_qubits):
    """Create a GHZ state on the given number of qubits."""
    prog = core.QProg()
    prog << core.H(0)
    for i in range(num_qubits - 1):
        prog << core.CNOT(i, i + 1)
    prog << core.measure(list(range(num_qubits)), list(range(num_qubits)))
    return prog

# Define a realistic noise model
noise = core.NoiseModel()
noise.add_all_qubit_quantum_error(
    core.depolarizing_error(0.002), core.GateType.H
)
noise.add_all_qubit_quantum_error(
    core.depolarizing_error(0.01), core.GateType.CNOT
)
noise.add_all_qubit_read_out_error([[0.97, 0.03], [0.04, 0.96]])

shots = 10000
machine = core.CPUQVM()

# Measure GHZ fidelity as a function of qubit count
for n in [2, 3, 4, 5, 6]:
    prog = build_ghz_circuit(n)

    machine.run(prog, shots=shots, model=noise)
    counts = machine.result().get_counts()

    # GHZ fidelity = fraction of results that are all-0 or all-1
    all_zero = "0" * n
    all_one = "1" * n
    ghz_count = counts.get(all_zero, 0) + counts.get(all_one, 0)
    fidelity = ghz_count / shots

    print(f"n={n}: GHZ fidelity={fidelity:.4f}  "
          f"all-0={counts.get(all_zero,0)}  "
          f"all-1={counts.get(all_one,0)}  "
          f"total_outcomes={len(counts)}")

输出将显示保真度随量子比特数迅速下降,因为 CNOT 门数量(从而累积噪声)线性增长:

n=2: GHZ fidelity=0.9750  ...
n=3: GHZ fidelity=0.9580  ...
n=4: GHZ fidelity=0.9310  ...
n=5: GHZ fidelity=0.9020  ...
n=6: GHZ fidelity=0.8640  ...

示例 5:噪声扫描分析

在开发算法时,对一系列噪声参数进行扫描以确定算法失效的阈值是很有用的。本示例在变分式电路上同时扫描去极化和振幅阻尼率。

python
from pyqpanda3 import core

def build_variational_circuit():
    """Build a simple variational-style ansatz circuit."""
    prog = core.QProg()
    # Layer 1
    prog << core.H(0) << core.H(1)
    prog << core.CNOT(0, 1)
    # Layer 2
    prog << core.RZ(0, 0.5) << core.RZ(1, 0.5)
    prog << core.CNOT(0, 1)
    # Layer 3
    prog << core.RZ(0, 0.3) << core.RZ(1, 0.3)
    prog << core.H(0) << core.H(1)
    prog << core.measure([0, 1], [0, 1])
    return prog

def compute_hamming_distance(counts, shots):
    """Compute the average Hamming weight of measurement outcomes."""
    total_weight = 0
    for bitstring, count in counts.items():
        weight = bitstring.count("1")
        total_weight += weight * count
    return total_weight / shots

prog = build_variational_circuit()
shots = 5000
machine = core.CPUQVM()

# First, get the ideal reference
machine.run(prog, shots=shots)
ideal_counts = machine.result().get_counts()
ideal_hw = compute_hamming_distance(ideal_counts, shots)

# Sweep depolarizing noise from 0.001 to 0.05
print("Depolarizing sweep:")
print(f"  {'Rate':>8s}  {'Hamming Weight':>15s}  {'Delta from Ideal':>17s}")
for rate in [0.001, 0.005, 0.01, 0.02, 0.05]:
    noise = core.NoiseModel()
    noise.add_all_qubit_quantum_error(
        core.depolarizing_error(rate), core.GateType.CNOT
    )
    noise.add_all_qubit_quantum_error(
        core.depolarizing_error(rate * 0.1), core.GateType.H
    )

    machine.run(prog, shots=shots, model=noise)
    counts = machine.result().get_counts()
    hw = compute_hamming_distance(counts, shots)
    delta = abs(hw - ideal_hw)
    print(f"  {rate:8.4f}  {hw:15.4f}  {delta:17.4f}")

# Sweep amplitude damping rate from 0.001 to 0.05
print("\nAmplitude damping sweep:")
print(f"  {'Rate':>8s}  {'Hamming Weight':>15s}  {'Delta from Ideal':>17s}")
for rate in [0.001, 0.005, 0.01, 0.02, 0.05]:
    noise = core.NoiseModel()
    noise.add_all_qubit_quantum_error(
        core.amplitude_damping_error(rate), core.GateType.CNOT
    )
    noise.add_all_qubit_quantum_error(
        core.amplitude_damping_error(rate * 0.5), core.GateType.H
    )

    machine.run(prog, shots=shots, model=noise)
    counts = machine.result().get_counts()
    hw = compute_hamming_distance(counts, shots)
    delta = abs(hw - ideal_hw)
    print(f"  {rate:8.4f}  {hw:15.4f}  {delta:17.4f}")

请注意,振幅阻尼在 Hamming 权重中产生偏差(系统性地推向更少的 1 位),而去极化噪声产生对称的扩散。这种不对称性对于依赖精确输出分布的算法非常重要。

示例 6:噪声下的密度矩阵层析

DensityMatrix 模拟器提供精确的态信息,非常适合详细的噪声分析。本示例重建并分析 Bell 态在不同噪声通道下的密度矩阵。

python
from pyqpanda3 import core
import numpy as np

# Build a Bell state circuit without measurement
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1)

# Reference: ideal density matrix
dm_sim = core.DensityMatrixSimulator()
dm_sim.run(prog)
ideal_dm = dm_sim.density_matrix()
ideal_probs = dm_sim.state_probs()

print("=== Ideal Bell State ===")
print(f"Probabilities: {ideal_probs}")
print(f"Purity: {np.trace(np.dot(ideal_dm, ideal_dm)):.6f}")
# Ideal Bell state has purity = 1.0

# Analyze each noise channel's effect on the density matrix
channels = {
    "Depolarizing (1%)":   core.depolarizing_error(0.01),
    "Amp. Damping (2%)":  core.amplitude_damping_error(0.02),
    "Phase Damping (2%)": core.phase_damping_error(0.02),
    "Pauli X (5%)":       core.pauli_x_error(0.05),
    "Pauli Z (5%)":       core.pauli_z_error(0.05),
}

print("\n=== Noise Channel Analysis ===")
for name, err in channels.items():
    noise = core.NoiseModel()
    noise.add_all_qubit_quantum_error(err, core.GateType.CNOT)
    noise.add_all_qubit_quantum_error(err, core.GateType.H)

    dm_sim.run(prog, noise)
    noisy_dm = dm_sim.density_matrix()
    noisy_probs = dm_sim.state_probs()

    # Compute purity: Tr(rho^2)
    purity = np.trace(np.dot(noisy_dm, noisy_dm))

    # Compute fidelity with ideal state
    # F(rho, sigma) = [Tr(sqrt(sqrt(rho) * sigma * sqrt(rho)))]^2
    # For pure ideal state |psi>: F = <psi|rho|psi>
    fidelity = np.real(ideal_dm[0, 0] * noisy_dm[0, 0]
                       + ideal_dm[0, 3] * noisy_dm[3, 0]
                       + ideal_dm[3, 0] * noisy_dm[0, 3]
                       + ideal_dm[3, 3] * noisy_dm[3, 3])

    print(f"\n{name}:")
    print(f"  P(00)={noisy_probs.get(0, 0):.4f}  "
          f"P(11)={noisy_probs.get(3, 0):.4f}  "
          f"P(01)={noisy_probs.get(1, 0):.4f}  "
          f"P(10)={noisy_probs.get(2, 0):.4f}")
    print(f"  Purity={purity:.4f}  Bell Fidelity={fidelity:.4f}")

从密度矩阵分析中可以得出以下关键要点:

  • 纯度在所有噪声通道下都降至 1.0 以下,量化了噪声引入了多少混合度。
  • Pauli Z 噪声降低纯度但不改变计算基概率,因为 Z|0=|0Z|1=|1。噪声仅影响非对角元素(相干项)。
  • 振幅阻尼破坏了 |00|11 之间的对称性,将概率推向 |00
  • 去极化噪声将概率泄漏均匀分布到所有非理想结果上。

NoiseOpType 枚举

NoiseOpType 枚举指定量子误差的内部表示:

python
# Kraus matrix representation
core.NoiseOpType.KARUS_MATRIICES

# Noise operation representation
core.NoiseOpType.NOISE

门相关噪声

真实量子处理器对不同门类型有不同的误差率。同一设备上 Hadamard 门的误差率可能为 5×104,而 CNOT 门可能为 2×102。准确建模这种门相关行为对于真实模拟至关重要。

为什么门相关噪声很重要

逐门类型噪声配置

以下示例演示如何在单个噪声模型中为不同门类型分配不同的误差通道和误差率:

python
from pyqpanda3 import core

def create_gate_dependent_noise():
    """Build a noise model with distinct error profiles per gate type."""
    noise = core.NoiseModel()

    # Single-qubit rotation gates have low depolarizing noise
    rot_error = core.depolarizing_error(0.0005)
    noise.add_all_qubit_quantum_error(rot_error, [
        core.GateType.H,
        core.GateType.X,
        core.GateType.Y,
        core.GateType.Z,
        core.GateType.S,
        core.GateType.T,
    ])

    # Parametric rotation gates have slightly higher noise due to
    # longer gate times and calibration uncertainty
    param_rot_error = core.depolarizing_error(0.001)
    noise.add_all_qubit_quantum_error(param_rot_error, [
        core.GateType.RX,
        core.GateType.RY,
        core.GateType.RZ,
    ])

    # CNOT is the noisiest gate on superconducting hardware
    cnot_error = core.depolarizing_error(0.02)
    noise.add_all_qubit_quantum_error(cnot_error, core.GateType.CNOT)

    # CZ gates may have different error characteristics than CNOT
    cz_error = core.depolarizing_error(0.015)
    noise.add_all_qubit_quantum_error(cz_error, core.GateType.CZ)

    # SWAP gates are decomposed into 3 CNOTs on most hardware
    swap_error = core.depolarizing_error(0.05)
    noise.add_all_qubit_quantum_error(swap_error, core.GateType.SWAP)

    return noise

noise = create_gate_dependent_noise()

# Build a circuit that uses multiple gate types
prog = core.QProg()
prog << core.H(0)
prog << core.RZ(1, 1.57)
prog << core.CNOT(0, 1)
prog << core.T(0)
prog << core.CZ(1, 2)
prog << core.measure([0, 1, 2], [0, 1, 2])

machine = core.CPUQVM()
machine.run(prog, shots=10000, model=noise)
print(f"Gate-dependent noise result: {machine.result().get_counts()}")

每个门组合多种误差通道

在真实硬件上,单个门可能同时受到多个独立噪声源的影响。你可以通过组合误差来建模这种情况:

python
from pyqpanda3 import core

noise = core.NoiseModel()

# For CNOT gates, model both coherent (depolarizing) and
# incoherent (amplitude damping) noise
dep_err = core.depolarizing_error(0.015)
amp_err = core.amplitude_damping_error(0.003)

# Apply depolarizing error to CNOT
noise.add_all_qubit_quantum_error(dep_err, core.GateType.CNOT)

# Also add amplitude damping on CNOT(0, 1)
noise.add_quantum_error(amp_err, core.GateType.CNOT, [0, 1])

# For single-qubit gates, add phase damping to model T2 processes
phase_err = core.phase_damping_error(0.002)
noise.add_all_qubit_quantum_error(phase_err, [
    core.GateType.H,
    core.GateType.T,
    core.GateType.S,
])

prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1) << core.T(0)
prog << core.measure([0, 1], [0, 1])

machine = core.CPUQVM()
machine.run(prog, shots=10000, model=noise)
print(f"Multi-channel noise: {machine.result().get_counts()}")

逐门噪声汇总

门类别典型误差率推荐通道
Clifford 单量子比特门(H、S、X)104 -- 103Depolarizing
非 Clifford 单量子比特门(T)103 -- 102Depolarizing
参数化旋转门(RX、RY、RZ)103 -- 5×103Depolarizing
双量子比特纠缠门(CNOT、CZ)102 -- 5×102Depolarizing + Amplitude Damping
SWAP(原生或分解的)3× CNOT 误差率Depolarizing
测量103 -- 102读出误差矩阵

噪声模型校准

为了使模拟具有预测性,需要从实际硬件表征数据推导噪声模型参数。本节介绍如何将原始校准指标转换为 pyqpanda3 的噪声模型参数。

从硬件指标到噪声参数

从 T1 推导振幅阻尼率

给定 T1 弛豫时间,对于持续时间为 tg 的门,振幅阻尼率 γ 为:

γ=1etg/T1

对于短门时间(tgT1),这近似为 γtg/T1

python
from pyqpanda3 import core
import math

def t1_to_amplitude_damping(t1_us, gate_time_ns):
    """Convert T1 time and gate duration to an amplitude damping rate.

    Args:
        t1_us: T1 relaxation time in microseconds
        gate_time_ns: gate duration in nanoseconds

    Returns:
        gamma: amplitude damping rate for use in amplitude_damping_error
    """
    # Convert to same units (microseconds)
    gate_time_us = gate_time_ns / 1000.0
    gamma = 1.0 - math.exp(-gate_time_us / t1_us)
    return gamma

# Example: T1 = 80 us, single-qubit gate time = 20 ns
gamma_1q = t1_to_amplitude_damping(t1_us=80, gate_time_ns=20)
print(f"1q amplitude damping rate: {gamma_1q:.6f}")

# Example: T1 = 80 us, CNOT gate time = 300 ns
gamma_2q = t1_to_amplitude_damping(t1_us=80, gate_time_ns=300)
print(f"2q amplitude damping rate: {gamma_2q:.6f}")

# Create the error channels
ad_1q = core.amplitude_damping_error(gamma_1q)
ad_2q = core.amplitude_damping_error(gamma_2q)

从 T2 推导相位阻尼率

给定 T2 退相位时间,对于持续时间为 tg 的门,相位阻尼率 λ 为:

λ=1etg/T2

注意 T2 必须满足 T22T1,这是由弛豫和退相位之间的基本关系决定的。

python
from pyqpanda3 import core
import math

def t2_to_phase_damping(t2_us, gate_time_ns):
    """Convert T2 time and gate duration to a phase damping rate.

    Args:
        t2_us: T2 dephasing time in microseconds
        gate_time_ns: gate duration in nanoseconds

    Returns:
        lambda_param: phase damping rate for use in phase_damping_error
    """
    gate_time_us = gate_time_ns / 1000.0
    lambda_param = 1.0 - math.exp(-gate_time_us / t2_us)
    return lambda_param

# Example: T2 = 60 us, single-qubit gate time = 20 ns
lambda_1q = t2_to_phase_damping(t2_us=60, gate_time_ns=20)
print(f"1q phase damping rate: {lambda_1q:.6f}")

# Example: T2 = 60 us, CNOT gate time = 300 ns
lambda_2q = t2_to_phase_damping(t2_us=60, gate_time_ns=300)
print(f"2q phase damping rate: {lambda_2q:.6f}")

# Create the error channels
pd_1q = core.phase_damping_error(lambda_1q)
pd_2q = core.phase_damping_error(lambda_2q)

构建校准噪声模型

以下函数演示如何从一组硬件校准参数组装完整的噪声模型:

python
from pyqpanda3 import core
import math

def build_calibrated_noise_model(
    t1_values,       # Dict[int, float]: qubit -> T1 in us
    t2_values,       # Dict[int, float]: qubit -> T2 in us
    gate_times,      # Dict[str, float]: gate name -> time in ns
    rb_fidelities,   # Dict[str, float]: gate name -> average gate fidelity
    readout_matrix,  # List[List[float]]: 2x2 assignment matrix
):
    """Build a noise model from hardware calibration data.

    All parameters come from standard characterization experiments:
    - T1/T2 from relaxation/echo experiments
    - Gate times from hardware specifications
    - RB fidelities from randomized benchmarking
    - Readout matrix from measurement confusion matrices
    """
    noise = core.NoiseModel()

    # Convert RB fidelity to depolarizing probability.
    # For a d-dimensional system, the relation is:
    #   F_avg = 1 - p * (d - 1) / d
    # For a single qubit (d=2): p = 2 * (1 - F_avg)
    for gate_name, fidelity in rb_fidelities.items():
        p_dep = 2.0 * (1.0 - fidelity)
        if gate_name == "H":
            noise.add_all_qubit_quantum_error(
                core.depolarizing_error(p_dep), core.GateType.H
            )
        elif gate_name == "CNOT":
            noise.add_all_qubit_quantum_error(
                core.depolarizing_error(p_dep), core.GateType.CNOT
            )

    # Add amplitude damping per qubit based on T1 and gate time
    for qubit, t1 in t1_values.items():
        t2 = t2_values.get(qubit, t1)
        for gate_name, gt_ns in gate_times.items():
            gate_time_us = gt_ns / 1000.0
            gamma = 1.0 - math.exp(-gate_time_us / t1)
            gate_type = core.GateType.H if gate_name == "H" else core.GateType.CNOT
            qubits = [qubit] if gate_name == "H" else [0, 1]
            noise.add_quantum_error(
                core.amplitude_damping_error(gamma), gate_type, qubits
            )
            # Add phase damping based on T2
            lambda_param = 1.0 - math.exp(-gate_time_us / t2)
            noise.add_quantum_error(
                core.phase_damping_error(lambda_param), gate_type, qubits
            )

    # Add readout errors from the assignment matrix
    noise.add_all_qubit_read_out_error(readout_matrix)

    return noise

# Example calibration data for a 2-qubit device
calibrated_noise = build_calibrated_noise_model(
    t1_values={0: 80.0, 1: 65.0},
    t2_values={0: 60.0, 1: 50.0},
    gate_times={"H": 20.0, "CNOT": 300.0},
    rb_fidelities={"H": 0.9995, "CNOT": 0.985},
    readout_matrix=[[0.97, 0.03], [0.04, 0.96]],
)

# Run a test circuit with the calibrated model
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1)
prog << core.measure([0, 1], [0, 1])

machine = core.CPUQVM()
machine.run(prog, shots=10000, model=calibrated_noise)
print(f"Calibrated noise result: {machine.result().get_counts()}")

噪声模拟工作流


噪声缓解策略

即使没有完整的量子纠错,几种实用技术也可以减少噪声对算法结果的影响。本节介绍可以在电路设计和后处理层面实施的策略。

策略概览

1. 最小化电路深度

最有效的策略是减少门的数量。每个门都是噪声进入的机会。在运行前使用编译和优化过程来降低深度。

python
from pyqpanda3 import core

# Inefficient: naive decomposition with many CNOT gates
prog_naive = core.QProg()
prog_naive << core.H(0)
prog_naive << core.CNOT(0, 1)
prog_naive << core.CNOT(1, 2)
prog_naive << core.CNOT(2, 3)
prog_naive << core.CNOT(3, 4)
prog_naive << core.measure([0, 1, 2, 3, 4], [0, 1, 2, 3, 4])

# Efficient: equivalent computation with fewer CNOT gates
# by reordering or exploiting circuit identities
prog_efficient = core.QProg()
prog_efficient << core.H(0)
prog_efficient << core.CNOT(0, 1)
prog_efficient << core.CNOT(0, 2)
prog_efficient << core.measure([0, 1, 2], [0, 1, 2])

# The efficient circuit has fewer two-qubit gates,
# resulting in lower accumulated noise

2. 使用原生门集

在面向真实硬件时,将电路编译为设备的原生门集。使用非原生门需要分解为多个原生门,从而放大噪声。

python
from pyqpanda3 import core

# Some devices natively support CZ instead of CNOT.
# Using the native gate avoids decomposition overhead.
prog = core.QProg()
prog << core.H(0) << core.H(1)
prog << core.CZ(0, 1)  # Use native CZ instead of decomposed CNOT
prog << core.measure([0, 1], [0, 1])

# Build a noise model that reflects the native gate's actual error rate
noise = core.NoiseModel()
noise.add_all_qubit_quantum_error(
    core.depolarizing_error(0.015), core.GateType.CZ
)
# No noise needed for CNOT since it is not used

3. 读出误差缓解

可以使用校准矩阵对测量误差进行表征和校正。其思想是准备每个计算基态,多次测量,并构建分配矩阵 M

ptrue=M1pmeasured
python
from pyqpanda3 import core
import numpy as np

def calibrate_readout(num_qubits, shots=10000):
    """Build a readout calibration matrix for the given qubits.

    For each basis state, prepare it and measure to find
    the probability of observing each outcome.
    """
    dim = 2 ** num_qubits
    cal_matrix = np.zeros((dim, dim))

    for basis_state in range(dim):
        # Prepare the basis state by applying X gates where bits are 1
        prog = core.QProg()
        for q in range(num_qubits):
            if (basis_state >> q) & 1:
                prog << core.X(q)
        prog << core.measure(list(range(num_qubits)), list(range(num_qubits)))

        machine = core.CPUQVM()
        machine.run(prog, shots=shots)
        counts = machine.result().get_counts()

        # Fill the column of the calibration matrix
        for outcome in range(dim):
            key = format(outcome, f"0{num_qubits}b")
            cal_matrix[outcome][basis_state] = counts.get(key, 0) / shots

    return cal_matrix

# Calibrate a 2-qubit readout
cal_matrix = calibrate_readout(num_qubits=2, shots=10000)
print(f"Calibration matrix:\n{cal_matrix}")

4. 零噪声外推

在多个人为放大的噪声水平下运行同一电路,然后外推回零噪声。这是近期设备最实用的误差缓解技术之一。

核心思想:如果噪声按 F(λ)=F0eαλ 缩放,其中 λ 是噪声缩放因子,则可以从 λ=1,2,3, 处的测量值估计 F0(无噪声结果)。

python
from pyqpanda3 import core
import numpy as np

def run_at_noise_scale(base_error_rate, scale_factor, shots=5000):
    """Run the benchmark circuit at an amplified noise level."""
    # Amplify the error rate by the scale factor
    amplified_rate = min(base_error_rate * scale_factor, 0.99)

    noise = core.NoiseModel()
    noise.add_all_qubit_quantum_error(
        core.depolarizing_error(amplified_rate), core.GateType.CNOT
    )

    prog = core.QProg()
    prog << core.H(0) << core.CNOT(0, 1)
    prog << core.measure([0, 1], [0, 1])

    machine = core.CPUQVM()
    machine.run(prog, shots=shots, model=noise)
    counts = machine.result().get_counts()

    # Return the Bell-state fidelity
    bell = (counts.get("00", 0) + counts.get("11", 0)) / shots
    return bell

# Measure at three noise levels and extrapolate linearly
base_rate = 0.02
scales = [1.0, 2.0, 3.0]
fidelities = [run_at_noise_scale(base_rate, s) for s in scales]

# Linear extrapolation to zero noise
# Fit: f(x) = a*x + b, estimate f(0) = b
coeffs = np.polyfit(scales, fidelities, deg=1)
f_zero_noise = coeffs[1]

print(f"Noise scale factors: {scales}")
print(f"Measured fidelities:  {fidelities}")
print(f"Extrapolated zero-noise fidelity: {f_zero_noise:.4f}")
# The extrapolated value should be closer to 1.0 than any individual measurement

缓解策略选择指南

策略开销有效性适用场景
最小化深度无(编译时)中等始终首先应用
原生门集无(编译时)中等面向特定硬件时
读出缓解O(2n) 校准运行对测量密集型电路效果高当读出误差占主导时
零噪声外推3× 电路运行对中等深度电路效果高通用缓解
概率误差消除随电路深度指数增长效果高但代价大小电路(< 20 量子比特)

最佳实践

1. 匹配硬件噪声

使用与目标设备匹配的误差率。超导设备的典型值:

误差类型典型率
单量子比特门103104
双量子比特门102103
读出102103
T1 时间20-100 μs

2. 小电路使用 DensityMatrixSimulator

对于少于约 10 个量子比特的电路,DensityMatrixSimulator 提供精确的噪声演化:

python
from pyqpanda3 import core

# Exact noise simulation for small circuits
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1)

noise_model = core.NoiseModel()
noise_model.add_all_qubit_quantum_error(
    core.depolarizing_error(0.01), core.GateType.CNOT
)

dm_sim = core.DensityMatrixSimulator()
dm_sim.run(prog, noise_model)
exact_probs = dm_sim.state_probs()
print(f"Exact probabilities: {exact_probs}")

3. 使用足够的采样次数

统计误差按 1/shots 下降:

  • 1000 次:约 3% 误差
  • 10000 次:约 1% 误差
  • 100000 次:约 0.3% 误差

4. 与理想模拟对比验证

始终将噪声结果与理想模拟进行比较,以理解噪声的影响:

python
from pyqpanda3 import core

# Build a Bell state circuit
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1)
prog << core.measure([0, 1], [0, 1])

# Create noise model
noise = core.NoiseModel()
noise.add_all_qubit_quantum_error(
    core.depolarizing_error(0.01), core.GateType.CNOT
)

machine = core.CPUQVM()

# Ideal simulation
machine.run(prog, shots=10000)
ideal_counts = machine.result().get_counts()

# Noisy simulation
machine.run(prog, shots=10000, model=noise)
noisy_counts = machine.result().get_counts()

# Compare
print(f"Ideal: {ideal_counts}")
print(f"Noisy: {noisy_counts}")

后续步骤

Released under the MIT License.