mirror of
https://github.com/K-Dense-AI/claude-scientific-skills.git
synced 2026-01-26 16:58:56 +08:00
516 lines
13 KiB
Markdown
516 lines
13 KiB
Markdown
# Noise Modeling and Mitigation
|
|
|
|
This guide covers noise models, noisy simulation, characterization, and error mitigation in Cirq.
|
|
|
|
## Noise Channels
|
|
|
|
### Depolarizing Noise
|
|
|
|
```python
|
|
import cirq
|
|
import numpy as np
|
|
|
|
# Single-qubit depolarizing channel
|
|
depol_channel = cirq.depolarize(p=0.01)
|
|
|
|
# Apply to qubit
|
|
q = cirq.LineQubit(0)
|
|
noisy_op = depol_channel(q)
|
|
|
|
# Add to circuit
|
|
circuit = cirq.Circuit(
|
|
cirq.H(q),
|
|
depol_channel(q),
|
|
cirq.measure(q, key='m')
|
|
)
|
|
```
|
|
|
|
### Amplitude Damping
|
|
|
|
```python
|
|
# Amplitude damping (T1 decay)
|
|
gamma = 0.1
|
|
amp_damp = cirq.amplitude_damp(gamma)
|
|
|
|
# Apply after gate
|
|
circuit = cirq.Circuit(
|
|
cirq.X(q),
|
|
amp_damp(q)
|
|
)
|
|
```
|
|
|
|
### Phase Damping
|
|
|
|
```python
|
|
# Phase damping (T2 dephasing)
|
|
gamma = 0.1
|
|
phase_damp = cirq.phase_damp(gamma)
|
|
|
|
circuit = cirq.Circuit(
|
|
cirq.H(q),
|
|
phase_damp(q)
|
|
)
|
|
```
|
|
|
|
### Bit Flip Noise
|
|
|
|
```python
|
|
# Bit flip channel
|
|
bit_flip_prob = 0.01
|
|
bit_flip = cirq.bit_flip(bit_flip_prob)
|
|
|
|
circuit = cirq.Circuit(
|
|
cirq.H(q),
|
|
bit_flip(q)
|
|
)
|
|
```
|
|
|
|
### Phase Flip Noise
|
|
|
|
```python
|
|
# Phase flip channel
|
|
phase_flip_prob = 0.01
|
|
phase_flip = cirq.phase_flip(phase_flip_prob)
|
|
|
|
circuit = cirq.Circuit(
|
|
cirq.H(q),
|
|
phase_flip(q)
|
|
)
|
|
```
|
|
|
|
### Generalized Amplitude Damping
|
|
|
|
```python
|
|
# Generalized amplitude damping
|
|
p = 0.1 # Damping probability
|
|
gamma = 0.2 # Excitation probability
|
|
gen_amp_damp = cirq.generalized_amplitude_damp(p=p, gamma=gamma)
|
|
```
|
|
|
|
### Reset Channel
|
|
|
|
```python
|
|
# Reset to |0⟩ or |1⟩
|
|
reset_to_zero = cirq.reset(q)
|
|
|
|
# Reset appears as measurement followed by conditional flip
|
|
circuit = cirq.Circuit(
|
|
cirq.H(q),
|
|
reset_to_zero
|
|
)
|
|
```
|
|
|
|
## Noise Models
|
|
|
|
### Constant Noise Model
|
|
|
|
```python
|
|
# Apply same noise to all qubits
|
|
noise = cirq.ConstantQubitNoiseModel(
|
|
qubit_noise_gate=cirq.depolarize(0.01)
|
|
)
|
|
|
|
# Simulate with noise
|
|
simulator = cirq.DensityMatrixSimulator(noise=noise)
|
|
result = simulator.run(circuit, repetitions=1000)
|
|
```
|
|
|
|
### Gate-Specific Noise
|
|
|
|
```python
|
|
class CustomNoiseModel(cirq.NoiseModel):
|
|
"""Apply different noise to different gate types."""
|
|
|
|
def noisy_operation(self, op):
|
|
# Single-qubit gates: depolarizing noise
|
|
if len(op.qubits) == 1:
|
|
return [op, cirq.depolarize(0.001)(op.qubits[0])]
|
|
|
|
# Two-qubit gates: higher depolarizing noise
|
|
elif len(op.qubits) == 2:
|
|
return [
|
|
op,
|
|
cirq.depolarize(0.01)(op.qubits[0]),
|
|
cirq.depolarize(0.01)(op.qubits[1])
|
|
]
|
|
|
|
return op
|
|
|
|
# Use custom noise model
|
|
noise_model = CustomNoiseModel()
|
|
simulator = cirq.DensityMatrixSimulator(noise=noise_model)
|
|
```
|
|
|
|
### Qubit-Specific Noise
|
|
|
|
```python
|
|
class QubitSpecificNoise(cirq.NoiseModel):
|
|
"""Different noise for different qubits."""
|
|
|
|
def __init__(self, qubit_noise_map):
|
|
self.qubit_noise_map = qubit_noise_map
|
|
|
|
def noisy_operation(self, op):
|
|
noise_ops = [op]
|
|
for qubit in op.qubits:
|
|
if qubit in self.qubit_noise_map:
|
|
noise = self.qubit_noise_map[qubit]
|
|
noise_ops.append(noise(qubit))
|
|
return noise_ops
|
|
|
|
# Define per-qubit noise
|
|
q0, q1, q2 = cirq.LineQubit.range(3)
|
|
noise_map = {
|
|
q0: cirq.depolarize(0.001),
|
|
q1: cirq.depolarize(0.005),
|
|
q2: cirq.depolarize(0.002)
|
|
}
|
|
|
|
noise_model = QubitSpecificNoise(noise_map)
|
|
```
|
|
|
|
### Thermal Noise
|
|
|
|
```python
|
|
class ThermalNoise(cirq.NoiseModel):
|
|
"""Thermal relaxation noise."""
|
|
|
|
def __init__(self, T1, T2, gate_time):
|
|
self.T1 = T1 # Amplitude damping time
|
|
self.T2 = T2 # Dephasing time
|
|
self.gate_time = gate_time
|
|
|
|
def noisy_operation(self, op):
|
|
# Calculate probabilities
|
|
p_amp = 1 - np.exp(-self.gate_time / self.T1)
|
|
p_phase = 1 - np.exp(-self.gate_time / self.T2)
|
|
|
|
noise_ops = [op]
|
|
for qubit in op.qubits:
|
|
noise_ops.append(cirq.amplitude_damp(p_amp)(qubit))
|
|
noise_ops.append(cirq.phase_damp(p_phase)(qubit))
|
|
|
|
return noise_ops
|
|
|
|
# Typical superconducting qubit parameters
|
|
T1 = 50e-6 # 50 μs
|
|
T2 = 30e-6 # 30 μs
|
|
gate_time = 25e-9 # 25 ns
|
|
|
|
noise_model = ThermalNoise(T1, T2, gate_time)
|
|
```
|
|
|
|
## Adding Noise to Circuits
|
|
|
|
### with_noise Method
|
|
|
|
```python
|
|
# Add noise to all operations
|
|
noisy_circuit = circuit.with_noise(cirq.depolarize(p=0.01))
|
|
|
|
# Simulate noisy circuit
|
|
simulator = cirq.DensityMatrixSimulator()
|
|
result = simulator.run(noisy_circuit, repetitions=1000)
|
|
```
|
|
|
|
### insert_into_circuit Method
|
|
|
|
```python
|
|
# Manual noise insertion
|
|
def add_noise_to_circuit(circuit, noise_model):
|
|
noisy_moments = []
|
|
for moment in circuit:
|
|
ops = []
|
|
for op in moment:
|
|
ops.extend(noise_model.noisy_operation(op))
|
|
noisy_moments.append(cirq.Moment(ops))
|
|
return cirq.Circuit(noisy_moments)
|
|
```
|
|
|
|
## Readout Noise
|
|
|
|
### Measurement Error Model
|
|
|
|
```python
|
|
class ReadoutNoiseModel(cirq.NoiseModel):
|
|
"""Model readout/measurement errors."""
|
|
|
|
def __init__(self, p0_given_1, p1_given_0):
|
|
# p0_given_1: Probability of measuring 0 when state is 1
|
|
# p1_given_0: Probability of measuring 1 when state is 0
|
|
self.p0_given_1 = p0_given_1
|
|
self.p1_given_0 = p1_given_0
|
|
|
|
def noisy_operation(self, op):
|
|
if isinstance(op.gate, cirq.MeasurementGate):
|
|
# Apply bit flip before measurement
|
|
noise_ops = []
|
|
for qubit in op.qubits:
|
|
# Average readout error
|
|
p_error = (self.p0_given_1 + self.p1_given_0) / 2
|
|
noise_ops.append(cirq.bit_flip(p_error)(qubit))
|
|
noise_ops.append(op)
|
|
return noise_ops
|
|
return op
|
|
|
|
# Typical readout errors
|
|
readout_noise = ReadoutNoiseModel(p0_given_1=0.02, p1_given_0=0.01)
|
|
```
|
|
|
|
## Noise Characterization
|
|
|
|
### Randomized Benchmarking
|
|
|
|
```python
|
|
import cirq
|
|
|
|
def generate_rb_circuit(qubits, depth):
|
|
"""Generate randomized benchmarking circuit."""
|
|
# Random Clifford gates
|
|
clifford_gates = [cirq.X, cirq.Y, cirq.Z, cirq.H, cirq.S]
|
|
|
|
circuit = cirq.Circuit()
|
|
for _ in range(depth):
|
|
for qubit in qubits:
|
|
gate = np.random.choice(clifford_gates)
|
|
circuit.append(gate(qubit))
|
|
|
|
# Add inverse to return to initial state (ideally)
|
|
# (simplified - proper RB requires tracking full sequence)
|
|
|
|
circuit.append(cirq.measure(*qubits, key='result'))
|
|
return circuit
|
|
|
|
# Run RB experiment
|
|
def run_rb_experiment(qubits, depths, repetitions=1000):
|
|
"""Run randomized benchmarking at various depths."""
|
|
simulator = cirq.DensityMatrixSimulator(
|
|
noise=cirq.ConstantQubitNoiseModel(cirq.depolarize(0.01))
|
|
)
|
|
|
|
survival_probs = []
|
|
for depth in depths:
|
|
circuits = [generate_rb_circuit(qubits, depth) for _ in range(20)]
|
|
|
|
total_survival = 0
|
|
for circuit in circuits:
|
|
result = simulator.run(circuit, repetitions=repetitions)
|
|
# Calculate survival probability (returned to |0⟩)
|
|
counts = result.histogram(key='result')
|
|
survival = counts.get(0, 0) / repetitions
|
|
total_survival += survival
|
|
|
|
avg_survival = total_survival / len(circuits)
|
|
survival_probs.append(avg_survival)
|
|
|
|
return survival_probs
|
|
|
|
# Fit to extract error rate
|
|
# p_survival = A * p^depth + B
|
|
# Error per gate ≈ (1 - p) / 2
|
|
```
|
|
|
|
### Cross-Entropy Benchmarking (XEB)
|
|
|
|
```python
|
|
def xeb_fidelity(circuit, simulator, ideal_probs, repetitions=10000):
|
|
"""Calculate XEB fidelity."""
|
|
|
|
# Run noisy simulation
|
|
result = simulator.run(circuit, repetitions=repetitions)
|
|
measured_probs = result.histogram(key='result')
|
|
|
|
# Normalize
|
|
for key in measured_probs:
|
|
measured_probs[key] /= repetitions
|
|
|
|
# Calculate cross-entropy
|
|
cross_entropy = 0
|
|
for bitstring, prob in measured_probs.items():
|
|
if bitstring in ideal_probs:
|
|
cross_entropy += prob * np.log2(ideal_probs[bitstring])
|
|
|
|
# Convert to fidelity
|
|
n_qubits = len(circuit.all_qubits())
|
|
fidelity = (2**n_qubits * cross_entropy + 1) / (2**n_qubits - 1)
|
|
|
|
return fidelity
|
|
```
|
|
|
|
## Noise Visualization
|
|
|
|
### Heatmap Visualization
|
|
|
|
```python
|
|
import matplotlib.pyplot as plt
|
|
|
|
def plot_noise_heatmap(device, noise_metric):
|
|
"""Plot noise characteristics across 2D grid device."""
|
|
|
|
# Get device qubits (assuming GridQubit)
|
|
qubits = sorted(device.metadata.qubit_set)
|
|
rows = max(q.row for q in qubits) + 1
|
|
cols = max(q.col for q in qubits) + 1
|
|
|
|
# Create heatmap data
|
|
heatmap = np.full((rows, cols), np.nan)
|
|
|
|
for qubit in qubits:
|
|
if isinstance(qubit, cirq.GridQubit):
|
|
value = noise_metric.get(qubit, 0)
|
|
heatmap[qubit.row, qubit.col] = value
|
|
|
|
# Plot
|
|
plt.figure(figsize=(10, 8))
|
|
plt.imshow(heatmap, cmap='RdYlGn_r', interpolation='nearest')
|
|
plt.colorbar(label='Error Rate')
|
|
plt.title('Qubit Error Rates')
|
|
plt.xlabel('Column')
|
|
plt.ylabel('Row')
|
|
plt.show()
|
|
|
|
# Example usage
|
|
noise_metric = {q: np.random.random() * 0.01 for q in device.metadata.qubit_set}
|
|
plot_noise_heatmap(device, noise_metric)
|
|
```
|
|
|
|
### Gate Fidelity Visualization
|
|
|
|
```python
|
|
def plot_gate_fidelities(calibration_data):
|
|
"""Plot single- and two-qubit gate fidelities."""
|
|
|
|
sq_fidelities = []
|
|
tq_fidelities = []
|
|
|
|
for qubit, metrics in calibration_data.items():
|
|
if 'single_qubit_rb_fidelity' in metrics:
|
|
sq_fidelities.append(metrics['single_qubit_rb_fidelity'])
|
|
if 'two_qubit_rb_fidelity' in metrics:
|
|
tq_fidelities.append(metrics['two_qubit_rb_fidelity'])
|
|
|
|
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
|
|
|
|
ax1.hist(sq_fidelities, bins=20)
|
|
ax1.set_xlabel('Single-Qubit Gate Fidelity')
|
|
ax1.set_ylabel('Count')
|
|
ax1.set_title('Single-Qubit Gate Fidelities')
|
|
|
|
ax2.hist(tq_fidelities, bins=20)
|
|
ax2.set_xlabel('Two-Qubit Gate Fidelity')
|
|
ax2.set_ylabel('Count')
|
|
ax2.set_title('Two-Qubit Gate Fidelities')
|
|
|
|
plt.tight_layout()
|
|
plt.show()
|
|
```
|
|
|
|
## Error Mitigation Techniques
|
|
|
|
### Zero-Noise Extrapolation
|
|
|
|
```python
|
|
def zero_noise_extrapolation(circuit, noise_levels, simulator):
|
|
"""Extrapolate to zero noise limit."""
|
|
|
|
expectation_values = []
|
|
|
|
for noise_level in noise_levels:
|
|
# Scale noise
|
|
noisy_circuit = circuit.with_noise(
|
|
cirq.depolarize(p=noise_level)
|
|
)
|
|
|
|
# Measure expectation
|
|
result = simulator.simulate(noisy_circuit)
|
|
# ... calculate expectation value
|
|
exp_val = calculate_expectation(result)
|
|
expectation_values.append(exp_val)
|
|
|
|
# Extrapolate to zero noise
|
|
from scipy.optimize import curve_fit
|
|
|
|
def exponential_fit(x, a, b, c):
|
|
return a * np.exp(-b * x) + c
|
|
|
|
popt, _ = curve_fit(exponential_fit, noise_levels, expectation_values)
|
|
zero_noise_value = popt[2]
|
|
|
|
return zero_noise_value
|
|
```
|
|
|
|
### Probabilistic Error Cancellation
|
|
|
|
```python
|
|
def quasi_probability_decomposition(noisy_gate, ideal_gate, noise_model):
|
|
"""Decompose noisy gate into quasi-probability distribution."""
|
|
|
|
# Decompose noisy gate as: N = ideal + error
|
|
# Invert: ideal = (N - error) / (1 - error_rate)
|
|
|
|
# This creates a quasi-probability distribution
|
|
# (some probabilities may be negative)
|
|
|
|
# Implementation depends on specific noise model
|
|
pass
|
|
```
|
|
|
|
### Readout Error Mitigation
|
|
|
|
```python
|
|
def mitigate_readout_errors(results, confusion_matrix):
|
|
"""Apply readout error mitigation using confusion matrix."""
|
|
|
|
# Invert confusion matrix
|
|
inv_confusion = np.linalg.inv(confusion_matrix)
|
|
|
|
# Get measured counts
|
|
counts = results.histogram(key='result')
|
|
|
|
# Convert to probability vector
|
|
total_counts = sum(counts.values())
|
|
measured_probs = np.array([counts.get(i, 0) / total_counts
|
|
for i in range(len(confusion_matrix))])
|
|
|
|
# Apply inverse
|
|
corrected_probs = inv_confusion @ measured_probs
|
|
|
|
# Convert back to counts
|
|
corrected_counts = {i: int(p * total_counts)
|
|
for i, p in enumerate(corrected_probs) if p > 0}
|
|
|
|
return corrected_counts
|
|
```
|
|
|
|
## Hardware-Based Noise Models
|
|
|
|
### From Google Calibration
|
|
|
|
```python
|
|
import cirq_google
|
|
|
|
# Get calibration data
|
|
processor = cirq_google.get_engine().get_processor('weber')
|
|
noise_props = processor.get_device_specification()
|
|
|
|
# Create noise model from calibration
|
|
noise_model = cirq_google.NoiseModelFromGoogleNoiseProperties(noise_props)
|
|
|
|
# Simulate with realistic noise
|
|
simulator = cirq.DensityMatrixSimulator(noise=noise_model)
|
|
result = simulator.run(circuit, repetitions=1000)
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
1. **Use density matrix simulator for noisy simulations**: State vector simulators cannot model mixed states
|
|
2. **Match noise model to hardware**: Use calibration data when available
|
|
3. **Include all error sources**: Gate errors, decoherence, readout errors
|
|
4. **Characterize before mitigating**: Understand noise before applying mitigation
|
|
5. **Consider error propagation**: Noise compounds through circuit depth
|
|
6. **Use appropriate benchmarking**: RB for gate errors, XEB for full circuit fidelity
|
|
7. **Visualize noise patterns**: Identify problematic qubits and gates
|
|
8. **Apply targeted mitigation**: Focus on dominant error sources
|
|
9. **Validate mitigation**: Verify that mitigation improves results
|
|
10. **Keep circuits shallow**: Minimize noise accumulation
|