Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 35 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,33 @@ tesseract-code-stim/
- **`notebooks/`**: Interactive Jupyter notebooks for experiments and visualization
- **`tests/`**: Comprehensive test suite covering all major functionality

## Results

### Error Correction Demonstration

First, we show the acceptance rates across different noise levels and number of error correction rounds:

![Acceptance Rates vs Error Correction Noise](plots/acceptance_rates_ec_experiment.png)

The plots below demonstrate the error correction procedure by comparing fidelity with and without Pauli frame correction applied. Both experiment run the full error correction rounds, but only the experiment plotted in the left plot applies the corrections based on the accumulated Pauli frame.

<table>
<tr>
<td align="center">
<img src="plots/fidelity_rates_ec_experiment_with_correction.png" alt="Fidelity rates with correction" width="400"/>
<br/>
<b>With Pauli Frame Correction</b>
</td>
<td align="center">
<img src="plots/fidelity_rates_ec_experiment_without_correction.png" alt="Fidelity rates without correction" width="400"/>
<br/>
<b>Without Pauli Frame Correction</b>
</td>
</tr>
</table>

This comparison clearly shows that the error correction procedure manages to correct some of the errors and achieve higher fidelity across different noise levels and number of error correction rounds.

## Quick Start

### Installation
Expand Down Expand Up @@ -174,13 +201,15 @@ The `plotting/plot_acceptance_rates.py` script generates acceptance and logical
python tesseract_sim/plotting/plot_acceptance_rates.py --apply_pauli_frame true --encoding-mode 9a --out-dir ./custom_plots
```

The script generates two types of plots:
- **Acceptance Rate Plots**: Show how well the error correction accepts states across different noise levels and rounds
- **Logical Success Rate Plots**: Show the conditional probability of logical success given acceptance

**Example Results:**
* **Customize rounds and noise levels:**
```bash
python tesseract_sim/plotting/plot_acceptance_rates.py --rounds 1 5 10 20 --noise-levels 0.01 0.05 0.1 --shots 1000
```

![Acceptance Rates vs Error Correction Noise](plots/acceptance_rates_ec_noise_ec_experiment.png)
The script generates three types of plots:
- **Acceptance Rate Plots**: Show how well the error correction accepts states across different noise levels and rounds
- **Logical Success Rate Plots**: Show the conditional probability of logical success given acceptance. Logical success is defined here as all qubits are measured to be in the correct state.
- **Fidelity Rate Plots**: Show the average fidelity of the measured logical state within the shots that were not rejected.

## References

Expand Down
Binary file added plots/acceptance_rates_ec_experiment.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed plots/acceptance_rates_ec_noise_ec_experiment.png
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Tesseract EC Experiment Metadata
===================================

Timestamp: 2025-09-16 16:54:26
Total runtime: 00:44:02.614 (2642.614 seconds)

Experiment Parameters:
--------------------
Rounds: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20]
Noise levels: [np.float64(0.0), np.float64(0.0011111111111111111), np.float64(0.0022222222222222222), np.float64(0.003333333333333333), np.float64(0.0044444444444444444), np.float64(0.005555555555555556), np.float64(0.006666666666666666), np.float64(0.0077777777777777776), np.float64(0.008888888888888889), np.float64(0.01)]
Shots per data point: 1000000
Apply Pauli frame correction: True
Encoding mode: 9a
Sweep channel noise: False
Noise configuration: Sweeping EC/decoding noise
- EC noise applied: During error correction rounds and decoding
- EC 1Q rate: Swept parameter
- EC 2Q rate: Swept parameter (same as 1Q)
- Channel noise: None (0.0)
- Encoding: Noiseless
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Tesseract EC Experiment Metadata
===================================

Timestamp: 2025-09-16 17:17:49
Total runtime: 00:23:22.613 (1402.613 seconds)

Experiment Parameters:
--------------------
Rounds: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20]
Noise levels: [np.float64(0.0), np.float64(0.0011111111111111111), np.float64(0.0022222222222222222), np.float64(0.003333333333333333), np.float64(0.0044444444444444444), np.float64(0.005555555555555556), np.float64(0.006666666666666666), np.float64(0.0077777777777777776), np.float64(0.008888888888888889), np.float64(0.01)]
Shots per data point: 1000000
Apply Pauli frame correction: False
Encoding mode: 9a
Sweep channel noise: False
Noise configuration: Sweeping EC/decoding noise
- EC noise applied: During error correction rounds and decoding
- EC 1Q rate: Swept parameter
- EC 2Q rate: Swept parameter (same as 1Q)
- Channel noise: None (0.0)
- Encoding: Noiseless
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
50 changes: 39 additions & 11 deletions tesseract_sim/error_correction/decoder_manual.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,30 @@ def verify_final_state(shot_tail, frameX=None, frameZ=None, apply_pauli_frame =

if apply_pauli_frame:
if frameX is not None and frameZ is not None:
# Top half measured in X basis - apply X frame corrections (only LSB matters)
# Top half measured in X basis - apply Z frame corrections (phase errors affect X measurements)
for i in range(8):
corrected[i] ^= (frameX[i] & 1)
corrected[i] ^= (frameZ[i] & 1)

# Bottom half measured in Z basis - apply Z frame corrections (only LSB matters)
# Bottom half measured in Z basis - apply X frame corrections (bit flips affect Z measurements)
# Account for CNOT propagation: X errors on qubits 0-3 propagate to 12-15, and 4-7 propagate to 8-11
for i in range(8, 16):
corrected[i] ^= (frameZ[i] & 1)
# Direct X frame corrections for qubits 8-15
corrected[i] ^= (frameX[i] & 1)

""" These workarounds are needed because we actually need to apply the correction before measurement.
A more complete and correct approach would be to branch insert the python error correction code before the final measurements
See https://quantumcomputing.stackexchange.com/questions/22281/simulating-flag-qubits-and-conditional-branches-using-stim
For more information.
"""
# CNOT propagation: X errors from row 1 (0-3) propagate to row 4 (12-15)
if 12 <= i <= 15:
source_qubit = i - 12 # qubit 12→0, 13→1, 14→2, 15→3
corrected[i] ^= (frameX[source_qubit] & 1)

# CNOT propagation: X errors from row 2 (4-7) propagate to row 3 (8-11)
if 8 <= i <= 11:
source_qubit = i - 4 # qubit 8→4, 9→5, 10→6, 11→7
corrected[i] ^= (frameX[source_qubit] & 1)

# Calculate all operator parities for both 8-3-2 color codes
# X measurements (top half, qubits 0-7)
Expand Down Expand Up @@ -147,25 +164,32 @@ def verify_final_state(shot_tail, frameX=None, frameZ=None, apply_pauli_frame =
def run_manual_error_correction(circuit, shots, rounds, apply_pauli_frame = True, encoding_mode ='9b'):
"""
Runs the full manual error correction simulation with final logical state verification.
Returns counts of shots that pass error correction and total successful parity checks.

Args:
circuit: The quantum circuit to simulate
shots: Number of shots to run
rounds: Number of error correction rounds
apply_pauli_frame: Whether to apply Pauli frame corrections
encoding_mode: '9a' or '9b' - determines measurement offset and which parity checks to perform

Returns:
tuple: (ec_accept, logical_shots_passed, average_percentage)
- ec_accept: number of successful experiments (i.e all rounds of ec "accept")
- logical_shots_passed: number of experiments when the final logical qubits measured had all qubits in the ideal state
- average_percentage: average percentage of qubits measured correctly across all shots
"""
# Calculate parameters based on encoding mode
only_z_checks = (encoding_mode == '9a')
measurement_offset = 0 if encoding_mode == '9a' else 2
max_checks = 2 if only_z_checks else 4

sampler = circuit.compile_sampler()
shot_data_all = sampler.sample(shots=shots)

ec_accept = 0
logical_shots_passed = 0
total_successful_checks = 0
fractional_logical_passed = 0.0

for shot_data in shot_data_all:
# Process error correction rounds with appropriate measurement offset
Expand All @@ -178,19 +202,23 @@ def run_manual_error_correction(circuit, shots, rounds, apply_pauli_frame = True
total_successful_checks += successful_checks

# Count shots where all parity checks pass
max_checks = 2 if only_z_checks else 4
if successful_checks == max_checks:
logical_shots_passed += 1

# Add fractional contribution for average percentage calculation
fractional_logical_passed += successful_checks / max_checks

# Calculate normalized logical success rate (total successful checks / total possible checks)
max_checks = 2 if only_z_checks else 4
normalized_logical_rate = total_successful_checks / (shots * max_checks) if shots > 0 else 0
# Calculate average percentage of qubits measured correctly
average_percentage = fractional_logical_passed / ec_accept if ec_accept > 0 else None

print(f"Correcting by Pauli frame → {apply_pauli_frame}")
print(f"After EC rounds → {ec_accept}/{shots} accepted")
checks_desc = "Z3,Z5" if only_z_checks else "X4,X6,Z3,Z5"
print(f"Total successful parity checks ({checks_desc}) → {total_successful_checks}/{shots * max_checks}")
print(f"Normalized logical success rate → {normalized_logical_rate:.2%}")
if average_percentage is not None:
print(f"Average percentage of checks passed → {average_percentage:.2%}")
else:
print(f"Average percentage of checks passed → N/A (no accepted shots)")
print(f"Logical shots passed (all checks) → {logical_shots_passed}/{shots}")

return ec_accept, logical_shots_passed, shots - logical_shots_passed
return ec_accept, logical_shots_passed, average_percentage
Loading