Skip to content

Introduce XZFaultList for tracking X and Z faults simultaneously - #783

Open
sunjerry019 wants to merge 23 commits into
mainfrom
XZFaultList_Fresh
Open

Introduce XZFaultList for tracking X and Z faults simultaneously#783
sunjerry019 wants to merge 23 commits into
mainfrom
XZFaultList_Fresh

Conversation

@sunjerry019

Copy link
Copy Markdown

Description

Expands on the idea of PureFaultSet by having a list (i.e. stable order) of coupled $X$- and $Z$-Faults on which gates such as CNOTs, H, Reset may be applied.

This PR is a fresh copy of the draft PR #712 without the edits to PureFaultSet, so that it can already be merged into main. Commits were picked from the branch XZFaultList.
In this case #690 need not be merged.

Checklist

  • The pull request only contains commits that are focused and relevant to this change.
  • I have added appropriate tests that cover the new/changed functionality.
  • I have updated the documentation to reflect these changes.
  • The changes follow the project's style guidelines and introduce no new warnings.
  • The changes are fully tested and pass the CI checks.
  • I have reviewed my own code changes.

If PR contains AI-assisted content:

  • I 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.
  • I confirm that I have personally reviewed and understood all AI-generated content, and accept full responsibility for it.

@sunjerry019

Copy link
Copy Markdown
Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added support for managing paired X/Z fault collections.
    • Added fault propagation through CNOT, Hadamard, reset, CCZ, and CCX operations.
    • Added validation, copying, iteration, formatted display, and optional in-place updates.
    • Added reduction of faults to coset leaders for X and Z generators.
  • Tests

    • Added comprehensive coverage for fault management, propagation, validation, reduction, and update behaviors.

Walkthrough

The PR adds public XZFaultList support for paired X/Z faults. It implements insertion, validation, copying, iteration, gate propagation, reset handling, coset-leader reduction, representation, and comprehensive tests.

Changes

XZ Fault List

Layer / File(s) Summary
Fault storage and validation
src/mqt/qecc/circuit_synthesis/faults.py, tests/circuit_synthesis/test_faults.py
XZFaultList stores paired X/Z arrays, supports single and batch insertion, substitutes missing components with zero faults, validates inputs, copies data, and preserves insertion order.
Gate fault propagation
src/mqt/qecc/circuit_synthesis/faults.py, tests/circuit_synthesis/test_faults.py
The class propagates faults through CNOT, Hadamard, reset, CCZ, and CCX operations. Tests cover transformations, validation, and in-place versus copied results.
Coset reduction and representation
src/mqt/qecc/circuit_synthesis/faults.py, tests/circuit_synthesis/test_faults.py
The class reduces X and Z faults independently with optional generators and provides structured string output. Tests cover generator shapes, empty inputs, in-place behavior, and representations.

Estimated code review effort: 4 (Complex) | ~45 minutes

Poem

A rabbit sees X faults hop with Z,
Through CNOT gates and CCZs.
Hadamards swap their place,
Resets clear the trace,
While coset leaders rest with ease.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary change: introducing XZFaultList for simultaneous X and Z fault tracking.
Description check ✅ Passed The description explains the change, motivation, context, tests, and AI assistance, while noting that documentation and CI checks remain incomplete.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch XZFaultList_Fresh

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🤖 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 `@src/mqt/qecc/circuit_synthesis/faults.py`:
- Around line 562-571: Update XZFaultList.add_faults in
src/mqt/qecc/circuit_synthesis/faults.py at lines 562-571 to validate both
inputs are 2D before accessing shape[1], and raise ValueError for 1D inputs;
then validate equal row counts before either np.vstack call and raise ValueError
when they differ. Add tests in tests/circuit_synthesis/test_faults.py at lines
713-752 covering mismatched row counts and 1D input, each asserting ValueError.
- Around line 771-789: Rename ensure_apply_valid_input to
_ensure_apply_valid_input and update every apply_* caller to use the private
helper name. Remove the boolean return contract and the final True return,
allowing the method to return None after successful validation while preserving
its existing ValueError checks.
- Around line 566-568: Update add_faults to validate that both x_faults and
z_faults are at least two-dimensional before accessing shape[1], raising the
documented ValueError for invalid dimensions; retain the existing num_qubits
column-count validation for valid-dimensional inputs.
- Around line 819-832: Update the fault-reduction method around ret, generators,
and coset_leader to validate and normalize both generator arrays before
modifying any faults, preserving atomic behavior for inplace=True. Pair
generators explicitly with the corresponding "X" and "Z" fault keys rather than
relying on zip iteration order, then perform reductions using those key
mappings. Add the runtime warning documented for
PureFaultSet.faults_to_coset_leaders to indicate that coset_leader is invoked
once per fault row.

In `@tests/circuit_synthesis/test_faults.py`:
- Around line 713-752: Update XZFaultList.add_faults validation to require X and
Z arrays have matching row counts, raising the documented ValueError before
appending data. Also validate that supplied arrays are two-dimensional so 1D
inputs raise ValueError rather than IndexError. Add tests alongside
test_add_faults_rejects_wrong_column_count covering mismatched rows and 1D
arrays.
- Around line 1038-1043: Update the assertions for the non-matching faults in
the relevant tests to compare the reduced fault rows against their expected
coset leaders, specifically asserting the deterministic zero vector for the
described X fault and checking index 2 in the second test. Replace the
non-informative row-count-only assertions while preserving the existing zero
checks for matching faults.
- Around line 904-951: Extend test_apply_ccz_unit_tests with at least one case
using a nonzero input Z on a control, and update expected Z/output assertions to
verify XOR cancellation behavior. Extend test_apply_ccx_unit_tests to exercise
inplace=True, asserting the returned object is the original faults instance and
that its X and Z data are mutated to the expected values.
🪄 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 Plus

Run ID: 714d31b5-134e-4e19-af37-4cee20bba0c8

📥 Commits

Reviewing files that changed from the base of the PR and between fef6ba7 and 3cdb4e1.

📒 Files selected for processing (2)
  • src/mqt/qecc/circuit_synthesis/faults.py
  • tests/circuit_synthesis/test_faults.py

Comment thread src/mqt/qecc/circuit_synthesis/faults.py
Comment thread src/mqt/qecc/circuit_synthesis/faults.py
Comment thread src/mqt/qecc/circuit_synthesis/faults.py Outdated
Comment thread src/mqt/qecc/circuit_synthesis/faults.py Outdated
Comment thread tests/circuit_synthesis/test_faults.py
Comment thread tests/circuit_synthesis/test_faults.py
Comment thread tests/circuit_synthesis/test_faults.py Outdated
@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@sunjerry019

sunjerry019 commented Aug 5, 2026

Copy link
Copy Markdown
Author

@lsschmid Could you look over this, and if it's okay, merge it? Thank you!

@lsschmid

lsschmid commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

I'll try to find the time the next days 👍

@lsschmid lsschmid left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

A few small things, mostly about staying consistent with PureFaultSet. Still thinking about the CCZ propagation model, will come back to that separately.


assert len(generators) == 2, "Generators should be a tuple of x_generators and z_generators"

logger.warning("This calls coset_leader once per fault row, so it might take a while.")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This fires on every call. The docstring already has a Warning section for it, so I'd either drop it or downgrade to logger.debug.


return ret

def __repr__(self) -> str:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

object.__repr__ puts the memory address in here, so the output isn't stable across runs. PureFaultSet.__repr__ just above returns a clean string, can we match that?

Comment on lines +622 to +633
if inplace:
# Apply CNOT directly to self.faults
x_faults, z_faults = self.faults["X"], self.faults["Z"]
ret = self
else:
# Create a new XZFaultList with copied faults
new_list = XZFaultList(self.num_qubits)
new_list.faults["X"] = np.copy(self.faults["X"])
new_list.faults["Z"] = np.copy(self.faults["Z"])

x_faults, z_faults = new_list.faults["X"], new_list.faults["Z"]
ret = new_list

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This copy-or-self block is repeated in apply_hadamard, apply_reset and apply_ccz. reduce_to_coset_leaders already does it as ret = self if inplace else self.copy(), would be nice to use that everywhere.

assert fault_set != PureFaultSet.from_fault_array(faults), "Faults were not permuted correctly in place"


"""XZFaultList Tests"""

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is a no-op expression, not a docstring. Make it a # comment.

Comment on lines +501 to +504
self.faults = {
"X": np.zeros((0, num_qubits), dtype=np.int8),
"Z": np.zeros((0, num_qubits), dtype=np.int8),
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The "X"/"Z" keys are typed as a plain dict[str, ...], so a typo like faults["Y"] only blows up at runtime. Two attributes (x_faults/z_faults, like the naming in state_prep.py) or a TypedDict would be safer. The tests also reach into faults.faults["X"] everywhere, so an accessor might be worth it.

new_list.faults["Z"] = np.copy(self.faults["Z"])
return new_list

def __iter__(self) -> Iterator[tuple[npt.NDArray[np.int8], npt.NDArray[np.int8]]]:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

There's __iter__ but no __len__. Also no __eq__, to_array or from_fault_array like PureFaultSet has. __eq__ especially will be needed as soon as something actually uses this class.

for i in range(len(self.faults["X"])):
yield (self.faults["X"][i], self.faults["Z"][i])

def apply_cnot(self, control: int, target: int, inplace: bool = True) -> XZFaultList:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

inplace defaults to True here, but PureFaultSet.combine defaults to False. Can we settle on one default for the module?

ValueError: If fault arrays don't have the correct length.
ValueError: If both faults are None
"""
assert len(faults) == 2, "Faults should be a tuple of x_fault and z_fault"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The type hint already fixes this to a 2-tuple, and asserts get stripped under -O. Same for the one in reduce_to_coset_leaders. Drop them, or raise a ValueError if you want the check to be real?

@lsschmid

Copy link
Copy Markdown
Collaborator

Hey! In general this looks good to me, thanks a lot for putting it together.

One thing: I don't know exactly what you need this for, but for the CCZ model some things are unclear to me.

z[c1] ^= x[c2] & x[c3] says a Z only appears when two controls have an X
fault. But a single X already leaves a CZ behind (CCZ·X₀·CCZ = X₀·CZ₁₂), which
acts as a Z depending on the other qubit's state. The & only works if the
qubits start in |0…0⟩. But the prep circuits also use |+⟩, so a lone X can cause a Z that
this misses. If the |0…0⟩ assumption is intended, could you put it in the
docstring?

Second, the ^= can cancel a real fault against an induced one: x=(0,1,1), z=(1,0,0) comes out with no Z at all (line 941, you noticed it too). Fine for
exact Pauli tracking, but the docstring calls this adversarial, and there it
hides a fault, so maybe it should be |=?!

if that is clarified I guess we can merge

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.

2 participants