Skip to content

Commit 6e7f3ac

Browse files
author
Stephen Shao
committed
Updated comments
1 parent e701cf1 commit 6e7f3ac

8 files changed

+1682
-177
lines changed

examples/module5_error_correction/01_quantum_noise_models.py

Lines changed: 155 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,43 @@
3535

3636

3737
def demonstrate_basic_noise_types():
38-
"""Demonstrate basic types of quantum noise."""
38+
"""
39+
Demonstrate basic types of quantum noise.
40+
41+
MATHEMATICAL BACKGROUND (For Beginners):
42+
==========================================
43+
Quantum noise happens because quantum states are fragile. When a qubit
44+
interacts with its environment, it loses information. There are three main types:
45+
46+
1. DEPOLARIZING NOISE - "Random Direction Errors"
47+
Mathematical Formula: ρ_noisy = (1-p)ρ + p·(I/2)
48+
Physical Meaning: With probability p, the qubit state gets replaced by
49+
completely random noise (total chaos!)
50+
Example: Like randomly flipping a coin - you lose all information
51+
52+
2. AMPLITUDE DAMPING - "Energy Loss" (T1 decay)
53+
Mathematical Formula: ρ_noisy = E₀·ρ·E₀† + E₁·ρ·E₁†
54+
where E₀ = [[1, 0], [0, √(1-γ)]], E₁ = [[0, √γ], [0, 0]]
55+
Physical Meaning: The excited state |1⟩ decays to ground state |0⟩
56+
Example: Like a battery slowly losing charge
57+
58+
3. PHASE DAMPING - "Phase Information Loss" (T2 dephasing)
59+
Mathematical Formula: ρ_noisy = (1-γ/2)ρ + (γ/2)Z·ρ·Z
60+
Physical Meaning: The relative phase between |0⟩ and |1⟩ gets randomized
61+
Example: Like two synchronized clocks slowly going out of sync
62+
63+
KEY INSIGHT: All three noise types affect superposition states differently!
64+
"""
3965
print("=== BASIC QUANTUM NOISE TYPES ===")
4066
print()
4167

42-
# Create a simple test circuit
68+
# ==================================================================
69+
# STEP 1: Create a simple test circuit
70+
# ==================================================================
71+
# We'll create a superposition state |+⟩ = (|0⟩ + |1⟩)/√2
72+
# This is the MOST SENSITIVE state to noise (it has both amplitudes and phase)
4373
qc = QuantumCircuit(1)
44-
qc.h(0) # Create superposition state
74+
qc.h(0) # Hadamard gate creates superposition: |0⟩ → (|0⟩ + |1⟩)/√2
4575

4676
initial_state = Statevector.from_instruction(qc)
4777
print(f"Initial state: {initial_state}")
@@ -50,29 +80,56 @@ def demonstrate_basic_noise_types():
5080
)
5181
print()
5282

53-
# Define different noise models
83+
# ==================================================================
84+
# STEP 2: Define different noise models
85+
# ==================================================================
5486
noise_models = {}
5587

56-
# 1. Depolarizing noise
57-
error_rate = 0.1
58-
depol_error = depolarizing_error(error_rate, 1)
88+
# -------------------------------------------------------------------
89+
# 1. DEPOLARIZING NOISE - "Complete Randomization"
90+
# -------------------------------------------------------------------
91+
# MATH: With probability p, replace state with I/2 (maximally mixed state)
92+
# ρ_out = (1-p)ρ + p·(I/2)
93+
# INTUITION: Imagine shaking a box with a coin - it becomes random!
94+
error_rate = 0.1 # 10% chance of noise per gate
95+
depol_error = depolarizing_error(error_rate, 1) # 1 = single-qubit error
5996
noise_model_depol = NoiseModel()
6097
noise_model_depol.add_all_qubit_quantum_error(depol_error, ["h"])
6198
noise_models["Depolarizing"] = noise_model_depol
6299

63-
# 2. Amplitude damping (T1 decay)
100+
# -------------------------------------------------------------------
101+
# 2. AMPLITUDE DAMPING - "Energy Decay" (Like a battery draining)
102+
# -------------------------------------------------------------------
103+
# MATH: Kraus operators E₀, E₁ describe energy loss
104+
# E₀ preserves |0⟩, partially preserves |1⟩
105+
# E₁ takes |1⟩ → |0⟩ (the decay process)
106+
# PHYSICAL MEANING: T1 = relaxation time (how long |1⟩ stays excited)
107+
# INTUITION: An atom in excited state falls back to ground state
64108
amp_damp_error = amplitude_damping_error(error_rate)
65109
noise_model_amp = NoiseModel()
66110
noise_model_amp.add_all_qubit_quantum_error(amp_damp_error, ["h"])
67111
noise_models["Amplitude Damping"] = noise_model_amp
68112

69-
# 3. Phase damping (T2 dephasing)
113+
# -------------------------------------------------------------------
114+
# 3. PHASE DAMPING - "Clock Desynchronization"
115+
# -------------------------------------------------------------------
116+
# MATH: Randomly applies Z gate (phase flip): |0⟩ stays, |1⟩ → -|1⟩
117+
# Superposition (|0⟩ + |1⟩) gradually loses phase coherence
118+
# PHYSICAL MEANING: T2 = dephasing time (how long phase info survives)
119+
# INTUITION: Two clocks ticking at slightly different rates
120+
# KEY: T2 ≤ 2T1 always (phase info more fragile than population)
70121
phase_damp_error = phase_damping_error(error_rate)
71122
noise_model_phase = NoiseModel()
72123
noise_model_phase.add_all_qubit_quantum_error(phase_damp_error, ["h"])
73124
noise_models["Phase Damping"] = noise_model_phase
74125

75-
# Test each noise model
126+
# ==================================================================
127+
# STEP 3: Test each noise model and measure the effects
128+
# ==================================================================
129+
# WHY MEASURE? To see how each noise type changes our superposition state
130+
# IDEAL RESULT: |+⟩ should give 50% |0⟩ and 50% |1⟩ when measured
131+
# NOISY RESULT: Different noise types will deviate differently!
132+
76133
simulator = AerSimulator()
77134
results = {}
78135

@@ -81,21 +138,26 @@ def demonstrate_basic_noise_types():
81138
test_circuit = qc.copy()
82139
test_circuit.measure_all()
83140

84-
# Run with noise
141+
# Run with noise (1000 shots = 1000 repetitions)
142+
# STATISTICS: More shots = more accurate probability estimates
143+
# Formula: Standard error ∝ 1/√(shots)
85144
job = simulator.run(
86145
transpile(test_circuit, simulator), shots=1000, noise_model=noise_model
87146
)
88147
result = job.result()
89148
counts = result.get_counts()
90149
results[noise_name] = counts
91150

92-
# Calculate probabilities
151+
# Calculate probabilities from measurement counts
152+
# MATH: Probability = (# of outcomes) / (total shots)
153+
# This is Born's rule: P(i) = |⟨i|ψ⟩|²
93154
prob_0 = counts.get("0", 0) / 1000
94155
prob_1 = counts.get("1", 0) / 1000
95156

96157
print(f"{noise_name} noise (error rate: {error_rate}):")
97158
print(f" Measured probabilities: |0⟩: {prob_0:.3f}, |1⟩: {prob_1:.3f}")
98159
print(f" Deviation from ideal: {abs(prob_0 - 0.5):.3f}")
160+
print(f" INTERPRETATION: Larger deviation = more noise damage!")
99161
print()
100162

101163
# Visualize results
@@ -124,54 +186,118 @@ def demonstrate_basic_noise_types():
124186

125187

126188
def analyze_error_rates():
127-
"""Analyze how different error rates affect quantum states."""
189+
"""
190+
Analyze how different error rates affect quantum states.
191+
192+
MATHEMATICAL CONCEPT (For Beginners):
193+
======================================
194+
FIDELITY = How similar two quantum states are
195+
196+
Mathematical Formula: F(ρ, σ) = Tr(√(√ρ σ √ρ))²
197+
For pure states: F = |⟨ψ|φ⟩|²
198+
199+
Range: 0 ≤ F ≤ 1
200+
- F = 1.0 means states are identical (perfect!)
201+
- F = 0.5 means states are somewhat similar
202+
- F = 0.0 means states are completely different
203+
204+
WHY IT MATTERS: Fidelity tells us how much damage noise has done.
205+
In quantum computing, we want F > 0.99 for useful computations!
206+
207+
EXPERIMENT GOAL: See how fidelity degrades as error rate increases
208+
"""
128209
print("=== ERROR RATE ANALYSIS ===")
129210
print()
130211

131-
# Create test circuit
212+
# ==================================================================
213+
# STEP 1: Create a test circuit - Bell state
214+
# ==================================================================
215+
# Bell state: |Φ+⟩ = (|00⟩ + |11⟩)/√2
216+
# This is a MAXIMALLY ENTANGLED state - very sensitive to noise!
217+
# MATH: Starting from |00⟩, apply H to first qubit, then CNOT
218+
# H|0⟩|0⟩ = (|0⟩+|1⟩)|0⟩/√2
219+
# CNOT gives (|00⟩+|11⟩)/√2 ← Bell state!
132220
qc = QuantumCircuit(2)
133-
qc.h(0)
134-
qc.cx(0, 1) # Create Bell state
221+
qc.h(0) # Create superposition on first qubit
222+
qc.cx(0, 1) # Entangle: if qubit 0 is |1⟩, flip qubit 1
135223

136224
ideal_state = Statevector.from_instruction(qc)
137225

138-
# Test different error rates
139-
error_rates = np.logspace(-3, -1, 10) # 0.001 to 0.1
226+
# ==================================================================
227+
# STEP 2: Test a range of error rates (from very small to large)
228+
# ==================================================================
229+
# We use logarithmic spacing: 0.1%, 0.2%, 0.5%, 1%, 2%, 5%, 10%
230+
# WHY LOGARITHMIC? Error rates span multiple orders of magnitude!
231+
error_rates = np.logspace(-3, -1, 10) # 10 points from 0.001 to 0.1
140232

233+
# Store fidelity results for each noise type
141234
fidelities = {"Depolarizing": [], "Amplitude Damping": [], "Phase Damping": []}
142235

236+
# Use density matrix method (needed for mixed states from noise)
237+
# MATH: Pure states → vectors |ψ⟩
238+
# Mixed states → density matrices ρ = Σᵢ pᵢ|ψᵢ⟩⟨ψᵢ|
143239
simulator = AerSimulator(method="density_matrix")
144240

241+
# ==================================================================
242+
# STEP 3: Loop through error rates and measure fidelity
243+
# ==================================================================
145244
for error_rate in error_rates:
146245
print(f"Testing error rate: {error_rate:.4f}")
147246

148247
for noise_type in fidelities.keys():
149-
# Create noise model
248+
# =============================================================
249+
# Create noise model for this error rate
250+
# =============================================================
251+
# IMPORTANT: We need separate noise models for 1-qubit and 2-qubit gates
252+
# WHY? Gates have different durations and complexities
253+
# TYPICAL: 2-qubit gates have ~10× higher error rates than 1-qubit gates
254+
150255
if noise_type == "Depolarizing":
151-
error_1q = depolarizing_error(error_rate, 1)
152-
error_2q = depolarizing_error(error_rate, 2)
256+
# DEPOLARIZING: Affects both single and two-qubit gates
257+
# MATH: ρ → (1-p)ρ + p·(I/d) where d = dimension (2 for qubits)
258+
error_1q = depolarizing_error(error_rate, 1) # Single-qubit H gate
259+
error_2q = depolarizing_error(error_rate, 2) # Two-qubit CNOT gate
153260
elif noise_type == "Amplitude Damping":
261+
# AMPLITUDE DAMPING: |1⟩ → |0⟩ energy decay
262+
# NOTE: Only defined for single qubits (it's a physical process)
154263
error_1q = amplitude_damping_error(error_rate)
155-
# For 2-qubit gates, use depolarizing as amplitude damping is 1-qubit only
264+
# For 2-qubit gates, approximate with depolarizing
156265
error_2q = depolarizing_error(error_rate, 2)
157266
else: # Phase Damping
267+
# PHASE DAMPING: Phase coherence loss
268+
# MATH: Random Z rotations destroy phase relationships
269+
# NOTE: Also only for single qubits
158270
error_1q = phase_damping_error(error_rate)
159-
# For 2-qubit gates, use depolarizing as phase damping is 1-qubit only
271+
# For 2-qubit gates, approximate with depolarizing
160272
error_2q = depolarizing_error(error_rate, 2)
161273

274+
# Build the complete noise model
162275
noise_model = NoiseModel()
163-
noise_model.add_all_qubit_quantum_error(error_1q, ["h"])
164-
noise_model.add_all_qubit_quantum_error(error_2q, ["cx"])
165-
166-
# Run simulation with noise - need to save and retrieve density matrix
276+
noise_model.add_all_qubit_quantum_error(error_1q, ["h"]) # H gate gets error_1q
277+
noise_model.add_all_qubit_quantum_error(error_2q, ["cx"]) # CNOT gets error_2q
278+
279+
# =============================================================
280+
# Run noisy simulation
281+
# =============================================================
282+
# TECHNICAL: We save the density matrix (not just measurements)
283+
# WHY? Density matrix ρ contains full info about mixed states
284+
# PURE STATE: ρ = |ψ⟩⟨ψ| (rank-1 matrix)
285+
# NOISY STATE: ρ = Σᵢ pᵢ|ψᵢ⟩⟨ψᵢ| (rank > 1, mixed state)
167286
qc_copy = qc.copy()
168-
qc_copy.save_density_matrix()
287+
qc_copy.save_density_matrix() # Tell simulator to save ρ
169288

170289
job = simulator.run(transpile(qc_copy, simulator), noise_model=noise_model)
171290
result = job.result()
172291
noisy_state = result.data()['density_matrix']
173292

174-
# Calculate fidelity
293+
# =============================================================
294+
# Calculate fidelity (how similar ideal vs noisy states are)
295+
# =============================================================
296+
# FORMULA: F = Tr(√(√ρ₁ ρ₂ √ρ₁))²
297+
# INTERPRETATION:
298+
# - F ≈ 1.0: Very little noise damage (excellent!)
299+
# - F ≈ 0.8-0.9: Moderate noise (usable)
300+
# - F < 0.7: High noise (problematic)
175301
fidelity = state_fidelity(ideal_state, noisy_state)
176302
fidelities[noise_type].append(fidelity)
177303

0 commit comments

Comments
 (0)