Skip to content

✨ add error correction module with Shor and Steane logical circuit transpilers#930

Draft
Felix-Gundlach wants to merge 86 commits into
munich-quantum-toolkit:mainfrom
emilkanic0909:feature/ErrorCorrection_MergePrep
Draft

✨ add error correction module with Shor and Steane logical circuit transpilers#930
Felix-Gundlach wants to merge 86 commits into
munich-quantum-toolkit:mainfrom
emilkanic0909:feature/ErrorCorrection_MergePrep

Conversation

@Felix-Gundlach

Copy link
Copy Markdown

Summary

This PR introduces a dedicated Error Correction Module for MQT Bench. It adds two high-level transpilers that encode standard benchmark circuits into fault-tolerant logical circuits using the 7-qubit Steane code and the 9-qubit Shor code, respectively. The module is designed to integrate cleanly with the existing benchmark_generation API.

Changes

New files

  • src/mqt/bench/error_correction/steane_transpiler.pySteaneTranspiler class: encodes logical qubits into 7-qubit Steane blocks, applies transversal Clifford gates (H, X, Z, S, CX, CZ), and handles T-gates via magic state injection with ancilla teleportation.
  • src/mqt/bench/error_correction/shor_transpiler.pyShorTranspiler class: encodes logical qubits into 9-qubit Shor blocks, implements the full logical Clifford gate set (including the non-transversal H via bit-swap), and supports S- and T-gates via magic state teleportation gadgets.
  • tests/test_error_correction.py — pytest suite covering gate-level equivalence (via MQT QCEC), correctness under injected bit- and phase-flip errors (via Hellinger fidelity with Aer), and circuit structure validation against pre-computed gate counts for GHZ, Bernstein–Vazirani, graph state, and QFT benchmarks.

How it works

Both transpilers follow the same three-phase pipeline:

  1. Encode — each logical qubit is replaced by a physical register (7 or 9 qubits) and initialized using the respective encoding circuit.
  2. Replace gates — the original circuit is scanned instruction by instruction and each gate is replaced with its logical equivalent. High-level gates (e.g. QFTGate) are first decomposed into the supported basis set {H, X, Z, S, T, CX, CZ}.
  3. Insert syndromes — after each logical operation, syndrome extraction and conditional correction cycles are appended using Qiskit's dynamic circuit if_test feature.

T-gates, which are non-transversal, are handled via a teleportation gadget: a magic state ancilla block is prepared, a logical CNOT is applied from the data qubit to the ancilla, the ancilla is decoded and measured, and a conditional logical S correction is applied on outcome 1.

Testing

  • Gate equivalence is verified using mqt.qcec for all single- and two-qubit Clifford gates.
  • Correctness under noise is verified by injecting a physical X or Z error after encoding and checking that the Hellinger fidelity between the corrected and uncorrected logical distributions is ≥ 0.99.
  • Circuit structure is validated against a reference gate_counts.json for qubit counts ranging from 3 to 9 across all supported algorithms.

Notes

  • QFT is currently excluded from the correctness simulation tests due to circuit depth making simulation infeasible.
  • Syndrome insertion can be toggled off via add_syndromes=False for lightweight structural or equivalence checks.

Checklist

  • The pull request only contains commits that are focused and relevant to this change.
  • We have added appropriate tests that cover the new/changed functionality.
  • We have updated the documentation to reflect these changes.
  • We have added entries to the changelog for any noteworthy additions, changes, fixes, or removals.
  • We have added migration instructions to the upgrade guide (if needed).
  • The changes follow the project's style guidelines and introduce no new warnings.
  • The changes are fully tested and pass the CI checks.
  • We have reviewed our own code changes.

If PR contains AI-assisted content:

  • We have disclosed the use of AI tools in the PR description as per our AI Usage Guidelines.
  • AI-assisted commits include an Assisted-by: [Model Name] via [Tool Name] footer.
  • We confirm that We have personally reviewed and understood all AI-generated content, and accept full responsibility for it.

@Felix-Gundlach

Copy link
Copy Markdown
Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

New Features

  • Added support for Shor's 9-qubit and Steane's 7-qubit error-correction encoding during benchmark generation via new encoding parameter
  • Introduced Shor and Steane transpilers for converting quantum circuits to fault-tolerant error-corrected implementations with logical gate support
  • Enabled syndrome extraction and conditional error correction for robust quantum operations

Tests

  • Added comprehensive error-correction tests including equivalence verification and fault injection analysis

Chores

  • Updated dependencies: mqt-qcec >= 3.6.1 and qiskit-aer >= 0.17.2
  • Updated .gitignore for development artifacts

Walkthrough

This PR introduces two complete error-correction transpilers (Shor 9-qubit and Steane 7-qubit codes) with shared circuit component libraries, integrates them into the benchmark API, refactors existing benchmarks to use the new components, and provides comprehensive test coverage with reference gate-count validation.

Changes

Error-Correction Transpilers and Components

Layer / File(s) Summary
Configuration and dependencies
.gitignore, pyproject.toml
Adds .idea/ and test-output directories to gitignore; adds runtime dependencies for mqt-qcec and qiskit-aer, and extends typos word-ignore list.
Steane circuit components
src/mqt/bench/components/steane_circuit_components.py
Implements 7-qubit Steane code circuit builders: encoding (fixed H/CNOT pattern), decoding (circuit inverse), syndrome extraction (3 bit-flip + 3 phase-flip ancillas), and conditional error correction via if_test.
Shor circuit components
src/mqt/bench/components/shor_circuit_components.py
Implements 3-qubit and 9-qubit Shor code helpers: phase/bit-flip encoding/decoding circuits, 5-qubit bit-flip syndrome extraction, 11-qubit phase-flip syndrome extraction, and conditional X/Z corrections based on syndrome patterns.
Steane transpiler implementation
src/mqt/bench/error_correction/steane_transpiler.py
Transpiles logical circuits into Steane 7-qubit fault-tolerant equivalents: allocates physical qubits per logical qubit, normalizes input gates (expands QFT), maps logical operations (h, x, z, s, t, cx, cz, measure, barrier) to physical implementations, and inserts optional syndrome extraction/correction.
Shor transpiler implementation
src/mqt/bench/error_correction/shor_transpiler.py
Transpiles logical circuits into Shor 9-qubit fault-tolerant equivalents: allocates 9 data + optional syndrome registers per logical qubit, normalizes gates, implements single-qubit gates transversally, realizes non-transversal S/T via magic-state teleportation gadgets with conditional correction, and manages syndrome tracking.
Benchmark generation API extension
src/mqt/bench/benchmark_generation.py
Extends get_benchmark_alg and get_benchmark with encoding parameter; conditionally routes circuits through ShorTranspiler or SteaneTranspiler when encoding="shor" or "steane" is specified.
Benchmark refactoring to use shared components
src/mqt/bench/benchmarks/seven_qubit_steane_code.py, shors_nine_qubit_code.py
Refactors Steane and Shor benchmarks to import and use the new shared circuit component helpers (encoding, syndrome extraction, correction, decoding) instead of duplicating logic locally.
Error-correction testing and validation
tests/test_error_correction.py, tests/gate_counts.json
Provides parametrized tests for gate equivalence (transpile and decode back to logical), gate-correction fidelity under injected errors (>0.99 Hellinger threshold), end-to-end benchmark correctness, and circuit-structure validation against reference gate counts stored in JSON.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant BenchAPI as Benchmark API
  participant Transpiler as Error-Correction<br/>Transpiler
  participant Encoder as Circuit<br/>Components
  participant Circuit as Output Circuit
  User->>BenchAPI: get_benchmark(encoding="steane")
  BenchAPI->>BenchAPI: Create logical circuit
  BenchAPI->>Transpiler: Transpile with encoding
  Transpiler->>Encoder: Call encode_qubits()
  Encoder->>Circuit: Physical qubits allocated
  Transpiler->>Transpiler: replace_gates()
  Transpiler->>Encoder: Insert syndromes
  Encoder->>Circuit: Add syndrome extraction/correction
  Transpiler->>Circuit: Return fault-tolerant circuit
  BenchAPI->>User: Steane-encoded benchmark
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • munich-quantum-toolkit/bench#803: Introduces the original Shor 9-qubit benchmark implementation that this PR refactors to use shared circuit component utilities.
  • munich-quantum-toolkit/bench#814: Introduces the original Steane 7-qubit benchmark implementation that this PR refactors to use shared circuit component utilities.

Suggested labels

feature, dependencies, python

Suggested reviewers

  • burgholzer

Poem

A rabbit hops through quantum gates,
With Shor and Steane, error-correction's fate!
Nine qubits dance, seven align,
Transversals weave where errors decline. 🐰✨
Syndromes extract, corrections apply—
Fault-tolerant circuits soar through the sky!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main addition: a new error correction module with two logical circuit transpilers (Steane and Shor).
Description check ✅ Passed The PR description comprehensively covers the changes, how it works, testing approach, and notes, with most checklist items clearly addressed or explained.
Docstring Coverage ✅ Passed Docstring coverage is 93.24% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 28

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/mqt/bench/benchmark_generation.py (1)

168-198: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Overload signatures missing the encoding parameter.

The three @overload declarations for get_benchmark_alg (lines 168-198) don't include the encoding: str = "" parameter that the implementation (line 204) declares. This breaks type-checker inference for callers, as the overloads define the externally visible signature.

Proposed fix: Add `encoding` to all overloads
 `@overload`
 def get_benchmark_alg(
     benchmark: str,
     circuit_size: int,
+    encoding: str = "",
     *,
     generate_mirror_circuit: bool = False,
     random_parameters: bool = True,
     **kwargs: Unpack[ConfigurationOptions],
 ) -> QuantumCircuit: ...


 `@overload`
 def get_benchmark_alg(
     benchmark: QuantumCircuit,
     circuit_size: None = None,
+    encoding: str = "",
     *,
     generate_mirror_circuit: bool = False,
     random_parameters: bool = True,
     **kwargs: Unpack[ConfigurationOptions],
 ) -> QuantumCircuit: ...


 `@overload`
 def get_benchmark_alg(
     benchmark: str | QuantumCircuit,
     circuit_size: int | None = None,
+    encoding: str = "",
     *,
     generate_mirror_circuit: bool = False,
     random_parameters: bool = True,
     **kwargs: Unpack[ConfigurationOptions],
 ) -> QuantumCircuit: ...

Also applies to: 204-204

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mqt/bench/benchmark_generation.py` around lines 168 - 198, The three
`@overload` declarations for get_benchmark_alg are missing the encoding parameter
present in the implementation; update each overload signature to include
encoding: str = "" (placed before the *kwargs / before the keyword-only params)
so the overloads exactly match the implementation's externally visible signature
for get_benchmark_alg.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pyproject.toml`:
- Around line 34-35: The pyproject.toml currently lists "mqt-qcec>=3.6.1" and
"qiskit-aer>=0.17.2" in the main dependencies; move these two entries into the
test dependency group (e.g., under [tool.poetry.group.test.dependencies] or the
project’s equivalent test/dev group) so they are only installed for tests,
leaving other dependencies unchanged; after moving, update the lockfile or run
the dependency manager’s install/lock command to refresh resolution and verify
tests (tests/test_error_correction.py) still run with the packages available in
the test group.

In `@src/mqt/bench/benchmark_generation.py`:
- Line 215: Update the docstrings for get_benchmark_alg
(src/mqt/bench/benchmark_generation.py lines 215-215) and get_benchmark
(src/mqt/bench/benchmark_generation.py lines 556-556) to remove the incorrect
"currently unused" note for the encoding parameter and instead document that
encoding selects the error-correction code used to transpile circuits (e.g.,
routing to ShorTranspiler when encoding == "shor" and to SteaneTranspiler when
encoding == "steane"), describe accepted encoding values and their effect on
circuit transpilation/output, and keep other parameter descriptions unchanged;
both locations require the same corrected description.
- Around line 228-237: The code silently returns the raw circuit when an invalid
encoding is passed; update the logic that checks the encoding variable (the
branch using ShorTranspiler and SteaneTranspiler) to explicitly validate allowed
values ("shor", "steane") and raise a clear exception (e.g., ValueError) or log
an error if an unsupported encoding is provided, rather than falling through to
return qc; ensure the error message references the invalid encoding value and
the accepted options and keep returning transpiler.transpiled_qc for valid cases
(use the existing ShorTranspiler, SteaneTranspiler, transpile(), and
transpiled_qc symbols).

In `@src/mqt/bench/benchmarks/shors_nine_qubit_code.py`:
- Around line 16-24: The import path in shors_nine_qubit_code.py is wrong;
update the import statement to use the correct module package name (components
plural) so the symbols apply_nine_qubit_shors_code_bit_flip_correction,
apply_nine_qubit_shors_code_phase_flip_correction,
get_nine_qubit_shors_code_phase_flip_syndrome_extraction_circuit,
get_three_qubit_bit_flip_encoding_decoding_circuit,
get_three_qubit_bit_flip_syndrome_extraction_circuit,
get_three_qubit_phase_flip_decoding_circuit, and
get_three_qubit_phase_flip_encoding_circuit are imported from the actual module
location (the shor_circuit_components module under the components package) —
replace the incorrect "mqt.bench.component.shor_circuit_components" import path
with the correct "mqt.bench.components.shor_circuit_components" so the names
resolve at runtime.

In `@src/mqt/bench/components/shor_circuit_components.py`:
- Around line 21-35: The function get_three_qubit_phase_flip_decoding_circuit is
actually used elsewhere (e.g., in
src/mqt/bench/benchmarks/shors_nine_qubit_code.py) so no deletion or refactor is
required; leave get_three_qubit_phase_flip_decoding_circuit() as-is. If you
prefer consistency you may optionally replace usages of
get_three_qubit_phase_flip_encoding_circuit().inverse() with a direct call to
get_three_qubit_phase_flip_decoding_circuit(), but this is optional and not
required to fix any bug.

In `@src/mqt/bench/components/steane_circuit_components.py`:
- Line 9: Replace the incorrect module docstring that reads "Shor's 9-qubit code
circuit components." with an accurate docstring such as "Steane's 7-qubit code
circuit components." so the top-of-file module documentation correctly reflects
that this module implements the 7-qubit Steane code components; update only the
module-level string literal (the triple-quoted docstring) in the file to the
corrected text.

In `@src/mqt/bench/error_correction/shor_transpiler.py`:
- Around line 20-21: Remove the confusing two-line inline comment ("# ignore the
below comment" and the following rationale) in shor_transpiler.py; either delete
both lines or replace them with a single clear module-level docstring or a
concise comment attached to the relevant functions explaining that these
functions are reused from the benchmark and are intentionally non-private (e.g.,
"These functions are reused from the benchmark and are public by design.").
Ensure the final text is clear and placed as module or function documentation
rather than an ambiguous inline note.
- Around line 345-348: The parameter names in _apply_logical_cx
(control_register, target_register) are misleading because the cx call uses them
in reversed order; either rename the parameters to reflect physical wire roles
(e.g., physical_control_register, physical_target_register) or add a brief
inline comment above the for-loop explaining that transpiled_qc.cx is invoked as
cx(physical_target, physical_control) to implement the transversal logical CX
inversion; update the function signature and any internal references (or add the
comment directly above the loop) so callers and future readers see the intended
ordering when inspecting _apply_logical_cx and the transpiled_qc.cx call.
- Around line 51-61: The get_all_registers method currently checks only
self.bit_flip_syndrome then unconditionally appends self.bit_flip_syndrome,
self.phase_flip_syndrome, self.bit_flip_measure, and self.phase_flip_measure
which can introduce None entries if those fields are not all set; change
get_all_registers to build the regs list starting with self.data and then append
or extend only those of (self.bit_flip_syndrome, self.phase_flip_syndrome,
self.bit_flip_measure, self.phase_flip_measure) that are not None (e.g., via an
explicit if per-field or a filtered list comprehension) so it’s defensive even
if encode_qubits no longer sets them all together.

In `@src/mqt/bench/error_correction/steane_transpiler.py`:
- Line 32: Change the __init__ signature in both transpiler classes to make
add_syndromes a keyword-only parameter: in
src/mqt/bench/error_correction/steane_transpiler.py modify
SteaneTranspiler.__init__ (line range 32-32) to def __init__(self,
original_circuit: QuantumCircuit, *, add_syndromes: bool = True) -> None:, and
in src/mqt/bench/error_correction/shor_transpiler.py modify
ShorTranspiler.__init__ (line range 67-67) to def __init__(self,
original_circuit: QuantumCircuit, *, add_syndromes: bool = True) -> None:; no
other call-site changes are required because callers already pass add_syndromes
by name.
- Around line 230-240: The inline comment in _handle_s is misspelled; change the
comment starting "# S Made cia SDG" to read "# S made via Sdg" (or similar
correct casing) in the _handle_s method to fix the typo; no code logic changes
required—only update the comment above the logical_qubit_index assignment and
keep the rest of the function (transpiled_qc.sdg, barrier, insert_syndromes)
intact.
- Line 41: Remove the dead/commented-out lines that reference
logical_qubit_measurements in this file (the commented assignment and any other
commented occurrences at the noted locations) — either delete them or, if the
feature is planned, open an issue and replace the comments with a short TODO
linking that issue; specifically remove or convert the commented lines
mentioning logical_qubit_measurements so the codebase has no commented-out
vestigial code.
- Line 173: Replace the TODO comment about measure_all() with an actionable
item: either document the known limitation in the surrounding function/class
docstring (e.g., add a note to the SteaneTranspiler class or the
transpile/transform method explaining that using measure_all() with the new
measurement register breaks the pipeline and describing the expected behavior),
or remove the TODO and create a tracked issue; if creating an issue, add a brief
"// TODO: tracked in issue #<number>" comment referencing the issue. Ensure the
change replaces the ambiguous TODO on line 173 with a concrete docstring note or
a TODO that points to the created issue.
- Around line 157-169: The _handle_barrier method builds barrier_register using
the loop index i instead of the actual logical qubit indices, so it picks the
wrong physical/ancilla registers; change the loop to iterate over the
instruction.qubits and for each qubit q compute its logical index with
self.original_qc.qubits.index(q) (as other handlers do), then use that index to
select self.physical_data_registers, self.bit_flip_syndromes, and
self.phase_flip_syndromes before extending barrier_register and finally call
self.transpiled_qc.barrier(*barrier_register, label="Barrier").

In `@tests/test_error_correction.py`:
- Around line 482-502: Update the parse_qubits function to use a complete
Google-style docstring: add an overall one-line summary then an Args section
describing qc (qk.QuantumCircuit) and physical_qubits (str) and a Returns
section describing the returned logical measurement bitstring (str); keep the
existing explanation about register naming ('qx' where x is an integer) and that
results are in qx[0] in the description. Ensure the docstring format matches
other functions (triple-quoted string immediately under def parse_qubits(...))
and includes types for each parameter and the return value.
- Around line 505-515: The function condense_counts currently has a short
docstring; replace it with a full Google-style docstring that describes the
function purpose and includes Args and Returns sections: describe qc
(qk.QuantumCircuit) as the circuit with named registers, counts (dict[str,int])
as the mapping of physical bitstrings to counts, and Returns as a dict mapping
logical measurement bitstrings to counts; mention the requirement that decode
places the logical qubit in the first qubit of registers named 'qx' and note
that parse_qubits(qc, physical_measurement) is used to compute the
logical_measurement.
- Around line 98-99: Remove the commented-out experimental code that inserts a
phase flip (the lines referencing error_induced_circuit and the
insert_error(..., gate=ZGate(), index=16) call); delete those commented lines
from the test so the file contains no commented-out experiments—if you want to
preserve the idea, create an issue or add a test skeleton referencing the
planned phase-flip test instead of leaving commented code in tests.
- Around line 406-414: The function check_equivalence is missing a Google-style
docstring with Args and Returns; update its docstring to the full Google-style
form describing parameters qc1 and qc2 (both qk.QuantumCircuit) and the boolean
return value, e.g., a one-line summary followed by an Args section listing qc1
and qc2 and a Returns section stating it returns True when the circuits are
equivalent/equivalent_up_to_global_phase/probably_equivalent and False
otherwise; keep the function name check_equivalence and its behavior unchanged.
- Around line 466-479: The compare_distributions function lacks a Google-style
docstring; add a complete docstring for compare_distributions that follows the
Google style including a one-line summary, a short description about condensing
counts when code1/code2 are 'steane' or 'shor', an Args section documenting qc1,
qc2, counts1, counts2, code1 (default "None") and code2 (default "None"), and a
Returns section describing the returned float (Hellinger fidelity: 1 =
identical, 0 = no overlap).
- Line 154: Update the pytest parametrization or surrounding test docstring to
explain why "qft" (Quantum Fourier Transform) is excluded: add a brief sentence
near the pytest.mark.parametrize("algorithm", ["ghz", "bv", "graphstate"]) line
or in the test function's docstring stating the specific limitation (for
example: "qft is excluded due to excessive circuit depth and simulation
time/memory requirements for the test input sizes"). This should reference "qft"
and the reason (circuit depth, simulation time, or memory) so future maintainers
understand the constraint.
- Line 157: The test's parameterization currently pins circuit_size to 3 via
`@pytest.mark.parametrize`("circuit_size", [3]) with the original range(3, 11)
commented out; update the test to document why only 3 is used by either adding a
brief inline comment above that decorator or expanding the test function's
docstring (the test that uses the circuit_size param) stating this is a
deliberate performance/memory limitation (or re-enable range(3, 11) if the
restraint was accidental), so reviewers can see the rationale for the
single-value parametrization.
- Around line 439-464: The run_circuit function is missing a Google-style
docstring Args section; update the docstring for run_circuit(qc: QuantumCircuit,
shots: int = 1024) to follow Google-style by adding an Args block documenting qc
(the QuantumCircuit that will be modified in-place to add measurements) and
shots (number of simulation shots, default 1024), and ensure the Returns section
clearly describes the tuple (measurement counts with reversed bitstrings and the
modified QuantumCircuit), matching the existing short description and behavior
in the function.
- Around line 305-308: Remove the redundant file operations: stop calling
json_data.close() (the with-context already closes the file) and simplify the
open call by using json_location.open("r", encoding="utf-8") directly instead of
Path(f"{json_location}"); keep the body as expected_gate_counts =
json.load(json_data).
- Line 372: The docstring currently says the parameter qc is "modified in place"
but omits that the function also returns the circuit; update the docstring to
state both behaviors (mutation and return) — e.g., change the qc parameter
description to "(modified in place and returned)" and/or add a Returns section
that explicitly says the mutated qc is returned, mirroring the phrasing used by
measure_all_named so readers know the function both mutates and returns the
circuit.
- Around line 385-386: Replace the two assert statements that check
qc.num_qubits >= gate.num_qubits and index is None or index >= 0 with explicit
runtime validation that always runs: if qc.num_qubits < gate.num_qubits: raise
ValueError(f"Quantum Circuit does not have enough qubits to accommodate gate
{gate.name} (needs {gate.num_qubits}, has {qc.num_qubits})"); and if index is
not None and index < 0: raise ValueError(f"Index must be >= 0, Index provided:
{index}"); keep the original error text/context in the messages and use the same
symbols (qc.num_qubits, gate.num_qubits, gate.name, index) so callers see the
same diagnostics.
- Around line 88-95: The Steane transpiler branch uses logical_circuit directly,
causing in-place modification; change the Steane branch to mirror the Shor
branch by constructing SteaneTranspiler(error_corrected_circuit,
add_syndromes=True) where error_corrected_circuit is set to
logical_circuit.copy() at the top (i.e., keep error_corrected_circuit =
logical_circuit.copy() and pass that copy into SteaneTranspiler), then call
transpile(), decode_qubits(), and assign transpiled_qc back to
error_corrected_circuit as done for the Shor path so both branches preserve the
original logical_circuit.
- Line 27: The import uses an unnecessary alias ("import
mqt.bench.benchmark_generation as benchmark_generation"); replace it with a
direct import that provides the same symbol name without aliasing—e.g., use
"from mqt.bench import benchmark_generation" (so existing uses of
benchmark_generation keep working) or simply "import
mqt.bench.benchmark_generation" and update call sites to reference
mqt.bench.benchmark_generation accordingly.
- Around line 46-48: The test currently skips the case when gate.name == "s" and
code == "shor" with a comment claiming S contains "non-unitary elements"; update
that comment to state the real reason: the Shor transpiler's logical-`S`
implementation (_logical_s → _apply_teleportation_gadget) uses an ancilla
teleportation gadget that inserts measure(...) and classically-controlled
corrections (if_test), so the generated circuit is not a purely-unitary circuit
and must be skipped for the "unitarily equivalent" check; keep the skip logic
(the return) unchanged and only replace the misleading comment text.

---

Outside diff comments:
In `@src/mqt/bench/benchmark_generation.py`:
- Around line 168-198: The three `@overload` declarations for get_benchmark_alg
are missing the encoding parameter present in the implementation; update each
overload signature to include encoding: str = "" (placed before the *kwargs /
before the keyword-only params) so the overloads exactly match the
implementation's externally visible signature for get_benchmark_alg.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: efbca649-80ab-4e6b-b3ba-e808cce91a01

📥 Commits

Reviewing files that changed from the base of the PR and between f47f3d1 and 33b24ce.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • .gitignore
  • pyproject.toml
  • src/mqt/bench/benchmark_generation.py
  • src/mqt/bench/benchmarks/seven_qubit_steane_code.py
  • src/mqt/bench/benchmarks/shors_nine_qubit_code.py
  • src/mqt/bench/components/shor_circuit_components.py
  • src/mqt/bench/components/steane_circuit_components.py
  • src/mqt/bench/error_correction/shor_transpiler.py
  • src/mqt/bench/error_correction/steane_transpiler.py
  • tests/gate_counts.json
  • tests/test_error_correction.py

Comment thread pyproject.toml
Arguments:
benchmark: QuantumCircuit or name of the benchmark to be generated
circuit_size: Input for the benchmark creation, in most cases this is equal to the qubit number
encoding: Error correction code to be used (currently unused).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Docstrings incorrectly state encoding is "currently unused".

Both get_benchmark_alg and get_benchmark docstrings describe the encoding parameter as "currently unused", but the parameter is actively used to route circuits through ShorTranspiler or SteaneTranspiler.

  • src/mqt/bench/benchmark_generation.py#L215-L215: Update get_benchmark_alg docstring to describe actual behavior.
  • src/mqt/bench/benchmark_generation.py#L556-L556: Update get_benchmark docstring to describe actual behavior.
📍 Affects 1 file
  • src/mqt/bench/benchmark_generation.py#L215-L215 (this comment)
  • src/mqt/bench/benchmark_generation.py#L556-L556
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mqt/bench/benchmark_generation.py` at line 215, Update the docstrings for
get_benchmark_alg (src/mqt/bench/benchmark_generation.py lines 215-215) and
get_benchmark (src/mqt/bench/benchmark_generation.py lines 556-556) to remove
the incorrect "currently unused" note for the encoding parameter and instead
document that encoding selects the error-correction code used to transpile
circuits (e.g., routing to ShorTranspiler when encoding == "shor" and to
SteaneTranspiler when encoding == "steane"), describe accepted encoding values
and their effect on circuit transpilation/output, and keep other parameter
descriptions unchanged; both locations require the same corrected description.

Comment on lines +228 to 237
if encoding == "shor":
transpiler = ShorTranspiler(qc, add_syndromes=True)
transpiler.transpile()
return transpiler.transpiled_qc
if encoding == "steane":
transpiler = SteaneTranspiler(qc, add_syndromes=True)
transpiler.transpile()
return transpiler.transpiled_qc

return qc

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Invalid encoding values silently fall through to return the unencoded circuit.

If a user passes an invalid encoding like encoding="shore" (typo), the function silently returns the raw circuit without any warning. Consider validating the parameter or documenting accepted values explicitly.

Proposed validation
+    valid_encodings = {"", "shor", "steane"}
+    if encoding not in valid_encodings:
+        msg = f"Invalid `encoding` '{encoding}'. Must be one of {valid_encodings}."
+        raise ValueError(msg)
+
     if encoding == "shor":
         transpiler = ShorTranspiler(qc, add_syndromes=True)
         transpiler.transpile()
         return transpiler.transpiled_qc
     if encoding == "steane":
         transpiler = SteaneTranspiler(qc, add_syndromes=True)
         transpiler.transpile()
         return transpiler.transpiled_qc

     return qc
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if encoding == "shor":
transpiler = ShorTranspiler(qc, add_syndromes=True)
transpiler.transpile()
return transpiler.transpiled_qc
if encoding == "steane":
transpiler = SteaneTranspiler(qc, add_syndromes=True)
transpiler.transpile()
return transpiler.transpiled_qc
return qc
valid_encodings = {"", "shor", "steane"}
if encoding not in valid_encodings:
msg = f"Invalid `encoding` '{encoding}'. Must be one of {valid_encodings}."
raise ValueError(msg)
if encoding == "shor":
transpiler = ShorTranspiler(qc, add_syndromes=True)
transpiler.transpile()
return transpiler.transpiled_qc
if encoding == "steane":
transpiler = SteaneTranspiler(qc, add_syndromes=True)
transpiler.transpile()
return transpiler.transpiled_qc
return qc
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mqt/bench/benchmark_generation.py` around lines 228 - 237, The code
silently returns the raw circuit when an invalid encoding is passed; update the
logic that checks the encoding variable (the branch using ShorTranspiler and
SteaneTranspiler) to explicitly validate allowed values ("shor", "steane") and
raise a clear exception (e.g., ValueError) or log an error if an unsupported
encoding is provided, rather than falling through to return qc; ensure the error
message references the invalid encoding value and the accepted options and keep
returning transpiler.transpiled_qc for valid cases (use the existing
ShorTranspiler, SteaneTranspiler, transpile(), and transpiled_qc symbols).

Comment on lines +16 to +24
from mqt.bench.component.shor_circuit_components import (
apply_nine_qubit_shors_code_bit_flip_correction,
apply_nine_qubit_shors_code_phase_flip_correction,
get_nine_qubit_shors_code_phase_flip_syndrome_extraction_circuit,
get_three_qubit_bit_flip_encoding_decoding_circuit,
get_three_qubit_bit_flip_syndrome_extraction_circuit,
get_three_qubit_phase_flip_decoding_circuit,
get_three_qubit_phase_flip_encoding_circuit,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the actual module path for shor_circuit_components
fd -t f "shor_circuit_components.py" --exec dirname {} \;

Repository: munich-quantum-toolkit/bench

Length of output: 99


Fix incorrect Shor circuit components import path
shors_nine_qubit_code.py imports from mqt.bench.component.shor_circuit_components, but shor_circuit_components.py is located under src/mqt/bench/components/, so the import will fail at runtime.

Proposed fix
-from mqt.bench.component.shor_circuit_components import (
+from mqt.bench.components.shor_circuit_components import (
     apply_nine_qubit_shors_code_bit_flip_correction,
     apply_nine_qubit_shors_code_phase_flip_correction,
     get_nine_qubit_shors_code_phase_flip_syndrome_extraction_circuit,
     get_three_qubit_bit_flip_encoding_decoding_circuit,
     get_three_qubit_bit_flip_syndrome_extraction_circuit,
     get_three_qubit_phase_flip_decoding_circuit,
     get_three_qubit_phase_flip_encoding_circuit,
 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from mqt.bench.component.shor_circuit_components import (
apply_nine_qubit_shors_code_bit_flip_correction,
apply_nine_qubit_shors_code_phase_flip_correction,
get_nine_qubit_shors_code_phase_flip_syndrome_extraction_circuit,
get_three_qubit_bit_flip_encoding_decoding_circuit,
get_three_qubit_bit_flip_syndrome_extraction_circuit,
get_three_qubit_phase_flip_decoding_circuit,
get_three_qubit_phase_flip_encoding_circuit,
)
from mqt.bench.components.shor_circuit_components import (
apply_nine_qubit_shors_code_bit_flip_correction,
apply_nine_qubit_shors_code_phase_flip_correction,
get_nine_qubit_shors_code_phase_flip_syndrome_extraction_circuit,
get_three_qubit_bit_flip_encoding_decoding_circuit,
get_three_qubit_bit_flip_syndrome_extraction_circuit,
get_three_qubit_phase_flip_decoding_circuit,
get_three_qubit_phase_flip_encoding_circuit,
)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mqt/bench/benchmarks/shors_nine_qubit_code.py` around lines 16 - 24, The
import path in shors_nine_qubit_code.py is wrong; update the import statement to
use the correct module package name (components plural) so the symbols
apply_nine_qubit_shors_code_bit_flip_correction,
apply_nine_qubit_shors_code_phase_flip_correction,
get_nine_qubit_shors_code_phase_flip_syndrome_extraction_circuit,
get_three_qubit_bit_flip_encoding_decoding_circuit,
get_three_qubit_bit_flip_syndrome_extraction_circuit,
get_three_qubit_phase_flip_decoding_circuit, and
get_three_qubit_phase_flip_encoding_circuit are imported from the actual module
location (the shor_circuit_components module under the components package) —
replace the incorrect "mqt.bench.component.shor_circuit_components" import path
with the correct "mqt.bench.components.shor_circuit_components" so the names
resolve at runtime.

Comment on lines +21 to +35
def get_three_qubit_phase_flip_decoding_circuit() -> QuantumCircuit:
"""Create 3-qubit phase-flip decoding circuit.

Reverses the phase-flip encoding.

Returns:
QuantumCircuit: 3-qubit decoding circuit (qubit 0 is the output qubit).
"""
out = QuantumCircuit(3)
out.h(0)
out.h(1)
out.h(2)
out.cx(0, 1)
out.cx(0, 2)
return out

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if get_three_qubit_phase_flip_decoding_circuit is used anywhere
echo "=== Searching for usages of get_three_qubit_phase_flip_decoding_circuit ==="
rg -n "get_three_qubit_phase_flip_decoding_circuit" --type=py

Repository: munich-quantum-toolkit/bench

Length of output: 483


Decoding helper is used; no dead-code cleanup needed
get_three_qubit_phase_flip_decoding_circuit is imported and called in src/mqt/bench/benchmarks/shors_nine_qubit_code.py (e.g., the decoding stage), so it isn’t unused. ShorTranspiler using get_three_qubit_phase_flip_encoding_circuit().inverse() is functionally equivalent; switching to the named decoding helper there would be an optional consistency refactor only.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mqt/bench/components/shor_circuit_components.py` around lines 21 - 35,
The function get_three_qubit_phase_flip_decoding_circuit is actually used
elsewhere (e.g., in src/mqt/bench/benchmarks/shors_nine_qubit_code.py) so no
deletion or refactor is required; leave
get_three_qubit_phase_flip_decoding_circuit() as-is. If you prefer consistency
you may optionally replace usages of
get_three_qubit_phase_flip_encoding_circuit().inverse() with a direct call to
get_three_qubit_phase_flip_decoding_circuit(), but this is optional and not
required to fix any bug.

Comment thread tests/test_error_correction.py
Comment thread tests/test_error_correction.py
Comment thread tests/test_error_correction.py
Comment thread tests/test_error_correction.py
Comment thread tests/test_error_correction.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants