Skip to content

Commit c35f50d

Browse files
Comparing plots (#9)
* Plot code now allows comparison between with/without correction * experiment_metadata now has original CLI command used for running the expriment * Added comparison plots
1 parent 7bcdb6f commit c35f50d

6 files changed

Lines changed: 310 additions & 56 deletions

File tree

149 KB
Loading
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
Tesseract EC Experiment Metadata
2+
===================================
3+
4+
Timestamp: 2025-09-23 23:19:41
5+
Command-line: tesseract_sim/plotting/plot_acceptance_rates.py --experiments 2 --noise-levels 0 0.001 0.002 0.004 0.006 0.008 0.01 --shots 1000000 --comparison-mode
6+
7+
Total runtime: 01:03:11.225 (3791.225 seconds)
8+
9+
Experiment Parameters:
10+
--------------------
11+
Rounds: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20]
12+
Noise levels: [0.0, 0.001, 0.002, 0.004, 0.006, 0.008, 0.01]
13+
Shots per data point: 1000000
14+
Apply Pauli frame correction: False
15+
Encoding mode: 9a
16+
Sweep channel noise: False
17+
Measurement error rate: 0.0
18+
Comparison mode: True
19+
Mode: Sweeping noise rates
20+
Noise configuration: Sweeping EC/decoding noise
21+
- EC noise applied: During error correction rounds and decoding
22+
- EC 1Q rate: Swept parameter
23+
- EC 2Q rate: Swept parameter (same as 1Q)
24+
- Channel noise: None (0.0)
25+
- Encoding: Noiseless
140 KB
Loading
136 KB
Loading

tesseract_sim/plotting/plot_acceptance_rates.py

Lines changed: 172 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
from datetime import datetime
99
import time
1010

11+
# Add imports for capturing the CLI invocation
12+
import sys, shlex
13+
1114
T = TypeVar('T') # Type of experiment result
1215

1316
def sweep_results(
@@ -85,13 +88,28 @@ def plot_curve(
8588
ylabel: str,
8689
out_path: str,
8790
xlim: Tuple[float, float] = None,
88-
ylim: Tuple[float, float] = None
91+
ylim: Tuple[float, float] = None,
92+
comparison_data: Dict[float, List[float]] = None,
93+
comparison_label: str = None
8994
) -> None:
90-
"""Plots and saves a single curve from sweep data."""
95+
"""Plots and saves a single curve from sweep data.
96+
97+
Args:
98+
comparison_data: Optional second dataset for comparison (plotted with dashed lines)
99+
comparison_label: Label suffix for comparison data
100+
"""
91101
plt.figure(figsize=(12, 8))
92102

103+
# Plot main data with solid lines
93104
for noise, rates in data.items():
94-
plt.plot(rounds, rates, marker='o', label=f'EC Noise Rate={noise:.4f}')
105+
plt.plot(rounds, rates, marker='o', linestyle='-',
106+
label=f'EC Noise Rate={noise:.4f}')
107+
108+
# Plot comparison data with dashed lines if provided
109+
if comparison_data is not None:
110+
for noise, rates in comparison_data.items():
111+
plt.plot(rounds, rates, marker='s', linestyle='--',
112+
label=f'EC Noise Rate={noise:.4f} ({comparison_label})')
95113

96114
plt.xlabel('Number of Rounds')
97115
plt.ylabel(ylabel)
@@ -122,15 +140,18 @@ def write_experiment_metadata(
122140
ec_rate_1q: float = None,
123141
ec_rate_2q: float = None,
124142
meas_error_rate: float = 0.0,
125-
channel_noise_rate: float = None
143+
channel_noise_rate: float = None,
144+
comparison_mode: bool = False
126145
) -> None:
127146
"""Write experiment metadata to a text file."""
128147
metadata_path = os.path.join(out_dir, "experiment_metadata.txt")
129148

130149
with open(metadata_path, 'w') as f:
131150
f.write("Tesseract EC Experiment Metadata\n")
132151
f.write("=" * 35 + "\n\n")
152+
# Record when and how this script was invoked
133153
f.write(f"Timestamp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
154+
f.write(f"Command-line: {shlex.join(sys.argv)}\n\n")
134155
if runtime_seconds is not None:
135156
hours = int(runtime_seconds // 3600)
136157
minutes = int((runtime_seconds % 3600) // 60)
@@ -147,6 +168,7 @@ def write_experiment_metadata(
147168
f.write(f"Encoding mode: {encoding_mode}\n")
148169
f.write(f"Sweep channel noise: {sweep_channel_noise}\n")
149170
f.write(f"Measurement error rate: {meas_error_rate}\n")
171+
f.write(f"Comparison mode: {comparison_mode}\n")
150172

151173
# Report if using fixed rates vs sweep
152174
use_fixed_rates = (ec_rate_1q is not None and ec_rate_2q is not None) or channel_noise_rate is not None
@@ -177,6 +199,91 @@ def write_experiment_metadata(
177199

178200
print(f"Metadata saved to {metadata_path}")
179201

202+
def _run_and_process(
203+
rounds: List[int],
204+
noise_levels: List[float],
205+
shots: int,
206+
cfg_builder: Callable[[float], NoiseCfg],
207+
encoding_mode: Literal['9a', '9b'],
208+
apply_pauli_frame: bool
209+
) -> Tuple[Dict[float, List[float]], Dict[float, List[float]], Dict[float, List[float]]]:
210+
"""
211+
Helper to run the EC experiment and process its results.
212+
Returns EC acceptance, logical success, and average fidelity.
213+
"""
214+
raw_results = sweep_results(
215+
run_simulation_ec_experiment,
216+
rounds, noise_levels, shots,
217+
cfg_builder,
218+
apply_pauli_frame=apply_pauli_frame,
219+
encoding_mode=encoding_mode
220+
)
221+
222+
ec_data = {
223+
noise: [t[0]/shots for t in tuples]
224+
for noise, tuples in raw_results.items()
225+
}
226+
227+
logical_data = compute_logical_success_rate(raw_results)
228+
229+
fidelity_data = compute_average_fidelity(raw_results)
230+
231+
return ec_data, logical_data, fidelity_data
232+
233+
def plot_metric(
234+
rounds: List[int],
235+
datasets: Dict[str, Dict[float, List[float]]],
236+
title: str,
237+
ylabel: str,
238+
out_path: str,
239+
xlim: Tuple[float, float] = None,
240+
ylim: Tuple[float, float] = None,
241+
styles: Dict[str, Tuple[str, str]] = None
242+
) -> None:
243+
"""
244+
Plots and saves a single metric (e.g., acceptance, logical success, fidelity)
245+
from sweep data, with optional comparison.
246+
"""
247+
plt.figure(figsize=(12, 8))
248+
# Assign consistent colors per noise value
249+
all_noises = set()
250+
for dataset in datasets.values():
251+
all_noises.update(dataset.keys())
252+
sorted_noises = sorted(all_noises)
253+
prop_cycle = plt.rcParams.get('axes.prop_cycle')
254+
colors = prop_cycle.by_key().get('color', []) if prop_cycle else []
255+
color_map = {noise: colors[i % len(colors)] for i, noise in enumerate(sorted_noises)}
256+
257+
# Plot each labeled dataset
258+
for label, dataset in datasets.items():
259+
linestyle, marker = styles.get(label, ('-', 'o')) if styles else ('-', 'o')
260+
for noise, rates in dataset.items():
261+
plt.plot(
262+
rounds,
263+
rates,
264+
color=color_map.get(noise),
265+
linestyle=linestyle,
266+
marker=marker,
267+
label=f'{noise:.4f} ({label})'
268+
)
269+
270+
plt.xlabel('Number of Rounds')
271+
plt.ylabel(ylabel)
272+
plt.title(title)
273+
plt.grid(True)
274+
plt.legend()
275+
276+
# Set axis limits if provided
277+
if xlim is not None:
278+
plt.xlim(xlim)
279+
if ylim is not None:
280+
plt.ylim(ylim)
281+
282+
plt.savefig(out_path)
283+
print(f"Plot saved to {out_path}")
284+
plt.close()
285+
286+
180287
def plot_ec_experiment(
181288
rounds: List[int],
182289
noise_levels: List[float],
@@ -188,17 +295,17 @@ def plot_ec_experiment(
188295
ec_rate_1q: float = None,
189296
ec_rate_2q: float = None,
190297
meas_error_rate: float = 0.0,
191-
channel_noise_rate: float = None
298+
channel_noise_rate: float = None,
299+
comparison_mode: bool = False
192300
) -> None:
193-
"""Plots both EC acceptance and logical check rates for the EC experiment."""
301+
"""Plots EC experiment curves, optionally comparing with/without Pauli-frame correction."""
194302
start_time = time.time()
195-
196303
# Create timestamped output directory
197304
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
198-
out_dir = os.path.join(base_out_dir, f"ec_experiment_{timestamp}")
305+
suffix = "_comparison" if comparison_mode else ""
306+
out_dir = os.path.join(base_out_dir, f"ec_experiment_{timestamp}{suffix}")
199307
os.makedirs(out_dir, exist_ok=True)
200-
201-
# One sweep collecting full results
308+
202309
# Determine if we're using fixed rates or sweeping
203310
use_fixed_rates = (ec_rate_1q is not None and ec_rate_2q is not None) or channel_noise_rate is not None
204311

@@ -246,71 +353,77 @@ def plot_ec_experiment(
246353
meas_error_rate=meas_error_rate
247354
)
248355

249-
raw_results = sweep_results(
250-
run_simulation_ec_experiment,
251-
rounds, noise_levels, shots,
252-
cfg_builder,
253-
apply_pauli_frame=apply_pauli_frame,
254-
encoding_mode=encoding_mode
356+
# Run sweeping and processing in helper
357+
ec_main, log_main, fid_main = _run_and_process(
358+
rounds, noise_levels, shots, cfg_builder, encoding_mode, apply_pauli_frame
255359
)
256360

257-
# Derive EC acceptance rate from raw results
258-
ec_data = {
259-
noise: [t[0]/shots for t in tuples] # ec_accept/shots
260-
for noise, tuples in raw_results.items()
261-
}
361+
# Prepare datasets and styles
362+
if comparison_mode:
363+
ec_comp, log_comp, fid_comp = _run_and_process(
364+
rounds, noise_levels, shots, cfg_builder, encoding_mode, not apply_pauli_frame
365+
)
366+
labels = ['with correction', 'without correction']
367+
datasets_accept = {
368+
labels[0]: ec_main,
369+
labels[1]: ec_comp,
370+
}
371+
datasets_logical = {
372+
labels[0]: log_main,
373+
labels[1]: log_comp,
374+
}
375+
datasets_fidelity = {
376+
labels[0]: fid_main,
377+
labels[1]: fid_comp,
378+
}
379+
styles = {
380+
labels[0]: ('-', 'o'),
381+
labels[1]: ('--', 's'),
382+
}
383+
else:
384+
label = 'with correction' if apply_pauli_frame else 'without correction'
385+
datasets_accept = {label: ec_main}
386+
datasets_logical = {label: log_main}
387+
datasets_fidelity = {label: fid_main}
388+
styles = {label: ('-', 'o')}
262389

263-
# Set fixed axis ranges
390+
# Plot each metric
264391
max_rounds = max(rounds)
265392
x_range = (0, max_rounds)
266-
267393
noise_type = "Channel" if sweep_channel_noise else "EC"
268-
plot_curve(
269-
rounds, ec_data,
270-
title=f"{noise_type} Acceptance vs Rounds (EC Experiment)",
394+
395+
plot_metric(
396+
rounds, datasets_accept,
397+
title=f"{noise_type} Acceptance vs Rounds (EC Experiment){' - Comparison' if comparison_mode else ''}",
271398
ylabel="EC Acceptance Rate",
272399
out_path=os.path.join(out_dir, 'acceptance_rates_ec_experiment.png'),
273-
xlim=x_range,
274-
ylim=(-0.01, 1.01)
400+
xlim=x_range, ylim=(-0.01, 1.01), styles=styles
275401
)
276-
277-
# Derive logical check rate from same raw results - normalized by acceptance
278-
logical_data = compute_logical_success_rate(raw_results)
279-
280-
plot_curve(
281-
rounds, logical_data,
282-
title=f"Logical Check Success vs Rounds (EC Experiment) - {noise_type} Noise",
402+
plot_metric(
403+
rounds, datasets_logical,
404+
title=f"Logical Check Success vs Rounds (EC Experiment) - {noise_type} Noise{' - Comparison' if comparison_mode else ''}",
283405
ylabel="Logical Success Rate | Accepted",
284406
out_path=os.path.join(out_dir, 'logical_rates_ec_experiment.png'),
285-
xlim=x_range,
286-
ylim=(-0.01, 1.01)
407+
xlim=x_range, ylim=(-0.01, 1.01), styles=styles
287408
)
288-
289-
# Derive average fidelity from same raw results
290-
fidelity_data = compute_average_fidelity(raw_results)
291-
292-
plot_curve(
293-
rounds, fidelity_data,
294-
title=f"Average Fidelity vs Rounds (EC Experiment) - {noise_type} Noise",
409+
plot_metric(
410+
rounds, datasets_fidelity,
411+
title=f"Average Fidelity vs Rounds (EC Experiment) - {noise_type} Noise{' - Comparison' if comparison_mode else ''}",
295412
ylabel="Average Fidelity",
296413
out_path=os.path.join(out_dir, 'fidelity_rates_ec_experiment.png'),
297-
xlim=x_range,
298-
ylim=(0.45, 1.01)
414+
xlim=x_range, ylim=(0.45, 1.01), styles=styles
299415
)
300-
301-
# Calculate total runtime and update metadata
302-
end_time = time.time()
303-
runtime_seconds = end_time - start_time
304-
416+
305417
# Write final metadata with runtime
418+
runtime_seconds = time.time() - start_time
306419
write_experiment_metadata(
307-
out_dir, rounds, noise_levels, shots,
420+
out_dir, rounds, noise_levels, shots,
308421
apply_pauli_frame, encoding_mode, sweep_channel_noise,
309422
runtime_seconds=runtime_seconds,
310-
ec_rate_1q=ec_rate_1q, ec_rate_2q=ec_rate_2q,
311-
meas_error_rate=meas_error_rate, channel_noise_rate=channel_noise_rate
423+
ec_rate_1q=ec_rate_1q, ec_rate_2q=ec_rate_2q,
424+
meas_error_rate=meas_error_rate, channel_noise_rate=channel_noise_rate,
425+
comparison_mode=comparison_mode
312426
)
313-
314427
print(f"All experiment files saved to: {out_dir}")
315428
print(f"Total experiment runtime: {runtime_seconds:.1f} seconds")
316429

@@ -352,6 +465,8 @@ def main():
352465
help='Measurement error rate (SPAM error)')
353466
parser.add_argument('--channel-noise-rate', type=float, default=None,
354467
help='Channel noise rate (overrides noise-levels sweep when using --sweep-channel-noise)')
468+
parser.add_argument('--comparison-mode', action='store_true',
469+
help='Run comparison between experiments with and without apply_pauli_frame')
355470
args = parser.parse_args()
356471

357472
# Use configurable values
@@ -365,7 +480,8 @@ def main():
365480
plot_ec_experiment(
366481
rounds, noise_levels, args.shots, args.out_dir,
367482
args.apply_pauli_frame, args.encoding_mode, args.sweep_channel_noise,
368-
args.ec_rate_1q, args.ec_rate_2q, args.meas_error_rate, args.channel_noise_rate
483+
args.ec_rate_1q, args.ec_rate_2q, args.meas_error_rate, args.channel_noise_rate,
484+
args.comparison_mode
369485
)
370486

371487
if __name__ == "__main__":

0 commit comments

Comments
 (0)