-
-
Notifications
You must be signed in to change notification settings - Fork 9
updates MPO class to allow construction of arbitrary Pauli Hamiltonians #216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
updates MPO class to allow construction of arbitrary Pauli Hamiltonians #216
Conversation
… via constructing the full matrix and SVD decomposition
📝 WalkthroughSummary by CodeRabbitRelease Notes
✏️ Tip: You can customize this high-level summary in your review settings. WalkthroughAdds Pauli-string parsing and term-based MPO construction (MPO.hamiltonian, MPO.from_pauli_sum), SVD-based MPO.compress with directional sweeps, and refactors legacy MPO initializers into builder-style APIs (ising, heisenberg, identity, custom, finite_state_machine). Tests now validate MPOs via dense mpo.to_matrix() comparisons. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User as Test / User
participant MPO as MPO.hamiltonian / MPO.from_pauli_sum
participant Parser as _parse_pauli_string
participant Builder as _create_term
participant Accumulator as _sum_terms
participant Compressor as _compress_one_sweep
User->>MPO: request construction (terms, L, tol, max_bond_dim, n_sweeps)
MPO->>Parser: parse Pauli-strings into site→op maps
Parser-->>MPO: parsed ops
MPO->>Builder: build MPO tensors for each term (with coeff)
Builder-->>Accumulator: submit term tensors
Accumulator-->>MPO: accumulated MPO tensors
loop n_sweeps
MPO->>Compressor: perform directional SVD sweep (direction, tol, max_bond_dim)
Compressor-->>MPO: updated tensors
end
MPO-->>User: return constructed (and compressed) MPO
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
📜 Recent review detailsConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro 📒 Files selected for processing (4)
🧰 Additional context used🧠 Learnings (3)📚 Learning: 2025-10-14T14:37:38.047ZApplied to files:
📚 Learning: 2025-12-05T17:45:37.602ZApplied to files:
📚 Learning: 2025-12-01T11:00:40.342ZApplied to files:
🪛 markdownlint-cli2 (0.18.1)CHANGELOG.md70-70: Link and image reference definitions should be needed (MD053, link-image-reference-definitions) ⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
🔇 Additional comments (7)
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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (2)
src/mqt/yaqs/core/data_structures/networks.py(1 hunks)tests/core/data_structures/test_networks.py(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tests/core/data_structures/test_networks.py (1)
src/mqt/yaqs/core/data_structures/networks.py (3)
MPO(958-1658)init_from_terms(1282-1341)to_matrix(1579-1601)
🪛 Ruff (0.14.0)
src/mqt/yaqs/core/data_structures/networks.py
1421-1421: Unused noqa directive (non-enabled: N803)
Remove unused noqa directive
(RUF100)
1483-1483: Unused noqa directive (non-enabled: N803)
Remove unused noqa directive
(RUF100)
1523-1523: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1524-1524: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1541-1541: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1547-1547: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1548-1548: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1549-1549: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1552-1552: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1555-1555: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1556-1556: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
- GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
- GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
- GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
🔇 Additional comments (1)
src/mqt/yaqs/core/data_structures/networks.py (1)
1282-1342: MPO.init_from_terms: solid construction and compression flowTerm-wise bond‑1 build, block‑diag sum, then local SVD sweeps is correct and matches Hubig et al. API/params read well. LGTM.
…d added test case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (1)
src/mqt/yaqs/core/data_structures/networks.py (1)
1505-1562: Remove unused noqa directives.The compression logic is correct, but there are multiple unused
# noqa: N806directives that should be removed.Apply this diff:
- A = self.tensors[k] # noqa: N806 - B = self.tensors[k + 1] # noqa: N806 + A = self.tensors[k] + B = self.tensors[k + 1] phys_dim = A.shape[0] bond_dim_left = A.shape[2] @@ -1540,9 +1540,9 @@ matrix = theta.reshape(bond_dim_left * phys_dim * phys_dim, phys_dim * phys_dim * bond_dim_right) # Apply SVD + truncation - U, S, Vh = np.linalg.svd(matrix, full_matrices=False) # noqa: N806 + U, S, Vh = np.linalg.svd(matrix, full_matrices=False) keep = int(np.sum(tol < S)) if max_bond_dim is not None: keep = min(keep, max_bond_dim) keep = max(1, keep) # keep at least one - U = U[:, :keep] # noqa: N806 # (bond_dim_L phys_dim^2) x bond_dim_trim - S = S[:keep] # noqa: N806 # bond_dim_trim - Vh = Vh[:keep, :] # noqa: N806 # bond_dim_trim x (phys_dim^2 bond_dim_R) + U = U[:, :keep] # (bond_dim_L phys_dim^2) x bond_dim_trim + S = S[:keep] # bond_dim_trim + Vh = Vh[:keep, :] # bond_dim_trim x (phys_dim^2 bond_dim_R) # Rebuild left tensor - UL = U.reshape(bond_dim_left, phys_dim, phys_dim, keep).transpose(1, 2, 0, 3) # noqa: N806 + UL = U.reshape(bond_dim_left, phys_dim, phys_dim, keep).transpose(1, 2, 0, 3) # Rebuild right tensor - SVh = (S[:, None] * Vh).reshape(keep, phys_dim, phys_dim, bond_dim_right) # noqa: N806 - VR = SVh.transpose(1, 2, 0, 3) # noqa: N806 + SVh = (S[:, None] * Vh).reshape(keep, phys_dim, phys_dim, bond_dim_right) + VR = SVh.transpose(1, 2, 0, 3)Based on static analysis hints.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (2)
src/mqt/yaqs/core/data_structures/networks.py(1 hunks)tests/core/data_structures/test_networks.py(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tests/core/data_structures/test_networks.py (1)
src/mqt/yaqs/core/data_structures/networks.py (3)
MPO(958-1660)init_from_terms(1282-1341)to_matrix(1581-1603)
🪛 Ruff (0.14.0)
src/mqt/yaqs/core/data_structures/networks.py
1416-1416: Unused noqa directive (non-enabled: N803)
Remove unused noqa directive
(RUF100)
1482-1482: Unused noqa directive (non-enabled: N803)
Remove unused noqa directive
(RUF100)
1525-1525: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1526-1526: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1543-1543: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1549-1549: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1550-1550: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1551-1551: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1554-1554: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1557-1557: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1558-1558: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
- GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
🔇 Additional comments (5)
src/mqt/yaqs/core/data_structures/networks.py (2)
1282-1342: LGTM! Well-implemented generic MPO construction.The
init_from_termsmethod correctly implements the Hubig et al. algorithm for constructing MPOs from Pauli strings. The logic handles edge cases (empty terms, single-site), validates input, and applies appropriate compression sweeps.
1371-1414: LGTM! Bond-1 MPO construction is correct.The implementation correctly builds bond-1 MPOs for product terms and applies the coefficient to the first site to avoid numerical overflow.
tests/core/data_structures/test_networks.py (3)
22-22: LGTM! Necessary import for test implementation.The
reduceimport is correctly used for constructing reference Hamiltonians via Kronecker products in the test functions below.
291-327: LGTM! Comprehensive test for multi-term MPO construction.The test correctly validates the MPO construction from a sum of Pauli strings by comparing against a Kronecker-product reference. Static assertions for length, physical dimension, and tensor count are appropriate.
329-349: LGTM! Important edge case test for single-site MPO.This test correctly validates the single-site MPO construction, which is an important edge case that was previously buggy (now fixed). The test ensures the special-case handling in
_mpo_sumworks as expected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (3)
src/mqt/yaqs/core/data_structures/networks.py (3)
1414-1414: Remove unused noqa directive.The
# noqa: N803directive on line 1414 is unnecessary and should be removed to satisfy the linter.Apply this diff:
- def _mpo_sum(A: list[np.ndarray], B: list[np.ndarray]) -> list[np.ndarray]: # noqa: N803 + def _mpo_sum(A: list[np.ndarray], B: list[np.ndarray]) -> list[np.ndarray]:Based on static analysis hints.
1480-1480: Remove unused noqa directive.The
# noqa: N803directive on line 1480 is unnecessary and should be removed to satisfy the linter.Apply this diff:
- def _mpo_product(A: list[np.ndarray], B: list[np.ndarray]) -> list[np.ndarray]: # noqa: N803 + def _mpo_product(A: list[np.ndarray], B: list[np.ndarray]) -> list[np.ndarray]:Based on static analysis hints.
1503-1560: Remove unused noqa directives and consider relative threshold.Multiple
# noqa: N806directives throughout this method are unnecessary and should be removed to satisfy the linter. Additionally, the current implementation uses an absolute threshold (tol < S), which may not scale well across different problem sizes. Consider using a relative threshold (tol * S[0] < S) to make the truncation scale-aware.Apply this diff to remove unused noqa directives:
- A = self.tensors[k] # noqa: N806 - B = self.tensors[k + 1] # noqa: N806 + A = self.tensors[k] + B = self.tensors[k + 1] phys_dim = A.shape[0] bond_dim_left = A.shape[2] @@ -1538,17 +1538,17 @@ matrix = theta.reshape(bond_dim_left * phys_dim * phys_dim, phys_dim * phys_dim * bond_dim_right) # Apply SVD + truncation - U, S, Vh = np.linalg.svd(matrix, full_matrices=False) # noqa: N806 + U, S, Vh = np.linalg.svd(matrix, full_matrices=False) keep = int(np.sum(tol < S)) if max_bond_dim is not None: keep = min(keep, max_bond_dim) keep = max(1, keep) # keep at least one - U = U[:, :keep] # noqa: N806 # (bond_dim_L phys_dim^2) x bond_dim_trim - S = S[:keep] # noqa: N806 # bond_dim_trim - Vh = Vh[:keep, :] # noqa: N806 # bond_dim_trim x (phys_dim^2 bond_dim_R) + U = U[:, :keep] + S = S[:keep] + Vh = Vh[:keep, :] # Rebuild left tensor - UL = U.reshape(bond_dim_left, phys_dim, phys_dim, keep).transpose(1, 2, 0, 3) # noqa: N806 + UL = U.reshape(bond_dim_left, phys_dim, phys_dim, keep).transpose(1, 2, 0, 3) # Rebuild right tensor - SVh = (S[:, None] * Vh).reshape(keep, phys_dim, phys_dim, bond_dim_right) # noqa: N806 - VR = SVh.transpose(1, 2, 0, 3) # noqa: N806 + SVh = (S[:, None] * Vh).reshape(keep, phys_dim, phys_dim, bond_dim_right) + VR = SVh.transpose(1, 2, 0, 3)Optional: To make the threshold scale-aware, consider changing line 1542:
- keep = int(np.sum(tol < S)) + keep = int(np.sum(tol * S[0] < S)) if len(S) > 0 and S[0] > 0 else len(S)Based on static analysis hints.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
src/mqt/yaqs/core/data_structures/networks.py(1 hunks)
🧰 Additional context used
🪛 Ruff (0.14.2)
src/mqt/yaqs/core/data_structures/networks.py
1414-1414: Unused noqa directive (non-enabled: N803)
Remove unused noqa directive
(RUF100)
1480-1480: Unused noqa directive (non-enabled: N803)
Remove unused noqa directive
(RUF100)
1523-1523: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1524-1524: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1541-1541: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1547-1547: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1548-1548: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1549-1549: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1552-1552: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1555-1555: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
1556-1556: Unused noqa directive (non-enabled: N806)
Remove unused noqa directive
(RUF100)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
- GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
- GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
🔇 Additional comments (1)
src/mqt/yaqs/core/data_structures/networks.py (1)
1369-1412: LGTM!The bond-1 MPO construction logic is correct and well-documented. The coefficient is appropriately applied to the first site tensor to avoid numerical overflow when summing many terms.
|
Added a MPO initializer |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In @src/mqt/yaqs/core/data_structures/networks.py:
- Around line 1354-1360: The current branch that handles an empty mpo_terms list
silently creates zero-valued tensors (setting self.tensors to zeros), which can
be surprising; update the constructor/initializer logic that processes mpo_terms
so that when mpo_terms is empty it either raises a clear exception (e.g.,
ValueError("mpo_terms must not be empty")) or emits a warning via the module
logger before returning the zero tensors. Locate the block that checks `if not
mpo_terms:` and replace the silent zero-initialization with the chosen behavior,
keeping the existing flow for the non-empty case that accumulates terms with
`_sum_terms`.
- Around line 990-997: The class-level mutable-looking attributes _PAULI_2,
_VALID, and _PAULI_TOKEN_RE should be annotated as typing.ClassVar to show they
are class-level constants; add from typing import ClassVar (if not already
imported) and change their declarations to use ClassVar[...] types (e.g.,
ClassVar[Dict[str, np.ndarray]] for _PAULI_2, ClassVar[FrozenSet[str]] for
_VALID, and ClassVar[re.Pattern] for _PAULI_TOKEN_RE) so static type checkers
understand these are class variables and not instance fields.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (2)
src/mqt/yaqs/core/data_structures/networks.pytests/core/data_structures/test_networks.py
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2026-01-08T10:07:23.393Z
Learnt from: flowerthrower
Repo: munich-quantum-toolkit/core-plugins-catalyst PR: 20
File: python/mqt/core/plugins/catalyst/device.py:69-69
Timestamp: 2026-01-08T10:07:23.393Z
Learning: In the munich-quantum-toolkit/core-plugins-catalyst repository, Ruff is configured with select = ["ALL"] and only specific rules are disabled. SLF001 (flake8-self - private member access) is enabled, so `# noqa: SLF001` directives are necessary when accessing private attributes like `device._to_matrix_ops` in python/mqt/core/plugins/catalyst/device.py, even if Ruff reports RUF100 warnings suggesting the directive is unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
📚 Learning: 2025-11-27T21:26:39.677Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/qmap PR: 846
File: python/mqt/qmap/plugins/qiskit/sc/load_calibration.py:34-34
Timestamp: 2025-11-27T21:26:39.677Z
Learning: In the qmap project, the Ruff linter has the "PL" (pylint) rule category enabled, which includes PLC0415 (import-outside-top-level). Therefore, `# noqa: PLC0415` directives on lazy imports are appropriate and necessary, not unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
🧬 Code graph analysis (2)
tests/core/data_structures/test_networks.py (2)
src/mqt/yaqs/core/libraries/gate_library.py (6)
GateLibrary(1630-1724)Id(744-763)X(602-621)Z(646-665)H(668-687)h(313-319)src/mqt/yaqs/core/data_structures/networks.py (5)
op(1285-1290)MPO(957-1616)init_ising(999-1037)to_matrix(1537-1559)init_heisenberg(1039-1079)
src/mqt/yaqs/core/data_structures/networks.py (1)
src/mqt/yaqs/core/libraries/gate_library.py (3)
Destroy(690-714)h(313-319)x(286-292)
🪛 Ruff (0.14.10)
src/mqt/yaqs/core/data_structures/networks.py
990-995: Mutable class attributes should be annotated with typing.ClassVar
(RUF012)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
- GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
- GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
🔇 Additional comments (12)
tests/core/data_structures/test_networks.py (3)
38-38: LGTM!The import change correctly removes
Ysince the tests now validate MPOs via dense matrix comparison rather than inspecting individual tensor blocks. The remaining imports (GateLibrary,Id,X,Z) are sufficient for the current test suite.
104-161: LGTM!The dense Hamiltonian helpers are well-implemented:
- Pauli matrices are correct
- Kronecker product embedding follows standard conventions
- Sign conventions (
-J,-g,-Jx, etc.) match the MPOinit_isingandinit_heisenbergimplementations
163-185: LGTM!The new tests properly validate MPO construction by comparing the full dense matrix against reference implementations. This approach is more robust than inspecting individual tensor blocks. The tolerance
atol=1e-12is appropriate for numerical precision.src/mqt/yaqs/core/data_structures/networks.py (9)
21-21: LGTM!Import of
reis appropriate for the new Pauli string parsing functionality.
28-28: LGTM!Import simplified to only
Destroysince Pauli matrices are now defined as class-level constants inMPO._PAULI_2.
1022-1037: LGTM!Good refactoring:
init_isingnow delegates to the generalhamiltonian()builder, reducing code duplication. Usingtype(self)enables proper subclass behavior.
1064-1079: LGTM!
init_heisenbergcorrectly delegates tohamiltonian()with appropriate two-body and one-body terms matching the original sign conventions.
1262-1314: LGTM!The
hamiltonian()classmethod is well-designed:
- Keyword-only arguments prevent positional errors
- Input validation is thorough
- Correct handling of open vs. periodic boundary conditions
- Clean delegation to
from_pauli_sum()for term construction
1461-1471: LGTM!Clean implementation of single-term MPO creation. The coefficient is correctly applied to the first tensor, and the copy prevents unintended mutation of the
_PAULI_2arrays.
1473-1490: LGTM!The Pauli string parser is robust with good error handling:
- Flexible input format (spaces or commas)
- Duplicate site detection
- Leftover token validation for malformed input
1492-1517: LGTM!The
_sum_termsmethod correctly implements MPO addition via bond dimension expansion with block-diagonal structure for interior tensors and appropriate boundary handling.One minor note: The
asserton line 1495 is acceptable for this internal method, but consider usingraise ValueErrorfor consistency with other validation in the module.
1441-1445: No issues found. The truncation logic at line 1442 correctly implements the documented behavior:keep = int(np.sum(tol < S))keeps singular values greater thantol, which discards values ≤tolas specified in the docstring. Thetolparameter is an absolute threshold (default1e-12), and the comparison direction is correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
🤖 Fix all issues with AI agents
In @src/mqt/yaqs/core/data_structures/networks.py:
- Around line 1592-1617: The _sum_terms function assumes matching physical
dimensions between MPO terms A and B but lacks a defensive check; add an
assertion at the start of _sum_terms (or inside the loop) to verify physical
dimension equality (e.g., assert A[0].shape[0] == B[0].shape[0], "Physical
dimensions must match") or assert a.shape[0] == b.shape[0] for each k, so
mismatched d values in arrays handled by _sum_terms are caught early.
- Around line 1410-1459: The compress method should validate that tol is
non-negative before running sweeps: inside compress (the public method that
calls _compress_one_sweep) add a check like raising ValueError with message "tol
must be non-negative." when tol < 0; this prevents the SVD truncation line in
_compress_one_sweep (keep = int(np.sum(tol < S))) from producing incorrect
behavior for negative tolerances and ensures callers must pass tol >= 0.
- Line 1093: The method definition heisenberg currently includes an unnecessary
"# noqa: N803" directive (same as ising); remove the trailing noqa from the def
heisenberg(self, length: int, Jx: float, Jy: float, Jz: float, h: float)
declaration so the function signature does not contain the unused lint
suppression.
- Line 1053: Remove the unused inline noqa directive from the ising function
signature: edit the def ising(...) declaration and delete the " # noqa: N803" at
the end so the function definition no longer contains that comment; ensure no
other references to N803 remain in the same declaration (function name ising).
- Around line 990-997: The class-level constants _PAULI_2, _VALID, and
_PAULI_TOKEN_RE should be annotated with typing.ClassVar to indicate they are
not instance attributes; update their declarations to use ClassVar[Dict[str,
np.ndarray]] (or appropriate types like ClassVar[frozenset] and
ClassVar[re.Pattern]) so the linter stops flagging RUF012 and static type
checkers treat them as constants.
In @tests/core/data_structures/test_networks.py:
- Around line 111-129: Add lightweight input validation to _embed_one_body and
_embed_two_body: assert that L is a non-negative int, that i is an int within
valid range (0 <= i < L) and for _embed_two_body also ensure i+1 < L, and assert
op (and op1/op2) are 2x2 numpy arrays (shape == (2,2)); raise AssertionError
with a clear message when checks fail so tests fail fast on bad inputs.
In @tests/core/methods/test_bug.py:
- Around line 98-100: The MPO.custom() call is using the default transpose which
flips the single-site tensor axes (mpo_tensor shape (2,2,1,1)); update the call
to pass transpose=False to preserve the supplied axis order (i.e., call
MPO.custom with transpose=False for mpo_tensor) so
prepare_canonical_site_tensors(mps, mpo) receives the correct MPO site shape and
canonicalization works as expected.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (12)
docs/examples/analog_simulation.mdsrc/mqt/yaqs/core/data_structures/networks.pysrc/mqt/yaqs/digital/digital_tjm.pysrc/mqt/yaqs/digital/equivalence_checker.pysrc/mqt/yaqs/digital/utils/mpo_utils.pytests/analog/test_analog_tjm.pytests/core/data_structures/test_networks.pytests/core/libraries/test_gate_library.pytests/core/methods/test_bug.pytests/core/methods/test_tdvp.pytests/digital/utils/test_mpo_utils.pytests/test_simulator.py
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2026-01-08T10:07:23.393Z
Learnt from: flowerthrower
Repo: munich-quantum-toolkit/core-plugins-catalyst PR: 20
File: python/mqt/core/plugins/catalyst/device.py:69-69
Timestamp: 2026-01-08T10:07:23.393Z
Learning: In the munich-quantum-toolkit/core-plugins-catalyst repository, Ruff is configured with select = ["ALL"] and only specific rules are disabled. SLF001 (flake8-self - private member access) is enabled, so `# noqa: SLF001` directives are necessary when accessing private attributes like `device._to_matrix_ops` in python/mqt/core/plugins/catalyst/device.py, even if Ruff reports RUF100 warnings suggesting the directive is unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
📚 Learning: 2025-11-27T21:26:39.677Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/qmap PR: 846
File: python/mqt/qmap/plugins/qiskit/sc/load_calibration.py:34-34
Timestamp: 2025-11-27T21:26:39.677Z
Learning: In the qmap project, the Ruff linter has the "PL" (pylint) rule category enabled, which includes PLC0415 (import-outside-top-level). Therefore, `# noqa: PLC0415` directives on lazy imports are appropriate and necessary, not unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
🧬 Code graph analysis (9)
tests/digital/utils/test_mpo_utils.py (1)
src/mqt/yaqs/core/data_structures/networks.py (1)
identity(1250-1265)
tests/core/libraries/test_gate_library.py (1)
src/mqt/yaqs/core/data_structures/networks.py (1)
custom(1294-1314)
tests/core/methods/test_tdvp.py (1)
src/mqt/yaqs/core/data_structures/networks.py (1)
ising(1053-1091)
tests/analog/test_analog_tjm.py (1)
src/mqt/yaqs/core/data_structures/networks.py (1)
ising(1053-1091)
tests/test_simulator.py (1)
src/mqt/yaqs/core/data_structures/networks.py (1)
ising(1053-1091)
src/mqt/yaqs/digital/equivalence_checker.py (1)
src/mqt/yaqs/core/data_structures/networks.py (1)
identity(1250-1265)
src/mqt/yaqs/digital/utils/mpo_utils.py (1)
src/mqt/yaqs/core/data_structures/networks.py (1)
custom(1294-1314)
src/mqt/yaqs/core/data_structures/networks.py (1)
src/mqt/yaqs/core/libraries/gate_library.py (3)
Destroy(690-714)x(286-292)h(313-319)
src/mqt/yaqs/digital/digital_tjm.py (1)
src/mqt/yaqs/core/data_structures/networks.py (1)
custom(1294-1314)
🪛 Ruff (0.14.10)
src/mqt/yaqs/core/data_structures/networks.py
990-995: Mutable class attributes should be annotated with typing.ClassVar
(RUF012)
1053-1053: Unused noqa directive (non-enabled: N803)
Remove unused noqa directive
(RUF100)
1093-1093: Unused noqa directive (non-enabled: N803)
Remove unused noqa directive
(RUF100)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: 🐍 Test (macos-14) / 🐍 macos-14
- GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
- GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
- GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
- GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
🔇 Additional comments (31)
docs/examples/analog_simulation.md (1)
31-31: LGTM! API rename correctly reflected in documentation.The update from
init_isingtoisingaligns with the new public API introduced in this PR.tests/test_simulator.py (1)
78-78: LGTM! Test updates correctly use the new API.All instances of
init_isinghave been properly updated toising, maintaining the same test logic and parameters.Also applies to: 132-132, 182-182, 466-466, 576-576, 690-690
src/mqt/yaqs/core/data_structures/networks.py (12)
21-21: LGTM! Import additions are appropriate.The
remodule is used for Pauli string parsing, andDestroyimport supports the transmon MPO initialization.Also applies to: 28-28
1000-1051: LGTM! Well-designed Hamiltonian builder with proper validation.The method correctly handles:
- Input validation (L > 0, valid boundary conditions)
- Open vs. periodic boundary condition logic
- Operator validation via the nested
op()helper- Delegation to
from_pauli_sumfor actual MPO constructionThe API design is clean and follows good practices.
1076-1091: LGTM! Cleaner implementation via delegation to the builder.The refactored
isingmethod correctly delegates to the newhamiltonianbuilder with appropriate Pauli terms for the Ising model and adopts the resulting MPO attributes.
1118-1133: LGTM! Heisenberg model correctly delegates to the builder.The implementation properly specifies all three Heisenberg coupling terms (XX, YY, ZZ) and the magnetic field term.
1250-1265: LGTM! Identity MPO initialization is correct.The method properly constructs identity MPO tensors with appropriate dimensions.
1267-1292: LGTM! Finite state machine MPO construction is correct.The method properly assembles the MPO from boundary and inner tensors with appropriate transposition and validation.
1294-1314: LGTM! Custom MPO initialization is clean and flexible.The keyword-only
transposeparameter is good API design, and the validation ensures correctness.
1316-1363: LGTM! Robust Pauli-sum MPO construction with thorough validation.The method includes:
- Proper input validation (dimension checks, length validation)
- Clear error messages for invalid operators and out-of-range site indices
- Correct handling of empty terms (zero MPO)
- Appropriate compression after construction
The implementation is well-designed and defensive.
1365-1408: LGTM! Well-designed compression API with flexible sweep scheduling.The method provides:
- Clear parameter validation
- Flexible direction schedules for different compression strategies
- Efficient early return for n_sweeps == 0
- Clean delegation to the internal sweep implementation
The design is both user-friendly and flexible.
1461-1477: LGTM! MPO rotation correctly swaps physical dimensions.The method properly handles both standard rotation and conjugate rotation for Hermitian conjugate operations.
1560-1571: LGTM! Clean implementation of product-state MPO term creation.The method correctly constructs bond-dimension-1 MPO terms with proper coefficient scaling.
1573-1590: LGTM! Robust Pauli string parsing with excellent validation.The parser includes:
- Flexible handling of whitespace and commas
- Regex-based token extraction
- Duplicate site detection
- Invalid token detection via clever regex substitution check
The implementation is both robust and user-friendly.
src/mqt/yaqs/digital/utils/mpo_utils.py (1)
279-279: LGTM! API rename correctly applied.The update from
init_customtocustomis consistent with the new MPO API, and thetranspose=Falseparameter is correctly preserved.tests/digital/utils/test_mpo_utils.py (1)
238-238: LGTM! Test updates correctly use the new API.All instances of
init_identityhave been properly updated toidentity, maintaining the same test logic.Also applies to: 260-260, 281-281
src/mqt/yaqs/digital/digital_tjm.py (2)
175-177: MPO.custom() usage is consistent with tensor layout here.
tensorsare constructed as (bond_left, bond_right, phys, phys), so relying oncustom()’s defaulttranspose=Trueis correct.
204-206: Good: transpose=False when re-wrapping existing MPO tensors.Prevents accidental reordering when slicing
mpo.tensors.tests/core/methods/test_bug.py (4)
83-85: Looks good: custom(..., transpose=False) keeps tensor ordering stable in tests.
122-124: Looks good: custom(..., transpose=False) matches random MPO tensor shapes used in this test.
255-257: LGTM: updated to renamed MPO.ising() API.
273-275: LGTM: updated to renamed MPO.ising() API.tests/analog/test_analog_tjm.py (1)
57-59: LGTM: tests consistently use renamed MPO.ising() constructor.Also applies to: 90-92, 124-126, 154-156, 184-186, 214-216, 255-257, 381-383
tests/core/methods/test_tdvp.py (1)
245-247: LGTM: updated to renamed MPO.ising() API.Also applies to: 274-276, 315-317, 349-351
tests/core/libraries/test_gate_library.py (2)
102-109: Good: custom(..., transpose=False) preserves expected tensor axis order for identity insertion assertions.
121-129: Good: transpose=False maintained for forward/reverse MPO tensor comparisons.src/mqt/yaqs/digital/equivalence_checker.py (1)
60-60: LGTM! Clean API migration.The rename from
init_identitytoidentityaligns with the new MPO initialization API. The method call correctly passes the number of qubits and uses the default physical dimension.tests/core/data_structures/test_networks.py (4)
104-108: Good practice: Test-independent Pauli definitions.Defining local Pauli matrices for the test helpers decouples the validation logic from the production GateLibrary implementation, which is appropriate for unit tests that verify correctness.
132-160: Dense Hamiltonian reference implementations are correct.These functions correctly construct the full dense matrices for Ising and Heisenberg models with open boundary conditions. The sign conventions are clearly documented and will be validated by the comparison tests.
163-185: Excellent: Matrix-equality validation for MPO correctness.These tests provide strong validation by comparing the MPO representation to exact dense Hamiltonians. This approach effectively catches operator ordering, sign errors, and structural issues in the MPO construction. The tolerance and system size are well-chosen for numerical testing.
188-339: LGTM! Consistent API migration across all tests.All test methods have been correctly updated to use the new MPO initialization API (
identity,ising,heisenberg,finite_state_machine,custom). The test logic remains unchanged, confirming this is a pure API surface update.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🤖 Fix all issues with AI agents
In @src/mqt/yaqs/core/data_structures/networks.py:
- Around line 990-997: Annotate the module-level/class-level constants to
silence RUF012: declare _PAULI_2 as a ClassVar[Dict[str, np.ndarray]] and _VALID
as a ClassVar[FrozenSet[str]] (and _PAULI_TOKEN_RE as ClassVar[Pattern[str]] if
needed), and add the corresponding imports (from typing import ClassVar, Dict,
FrozenSet, Pattern) so linters treat them as class-level constants rather than
instance attributes; update the type hints for the symbols _PAULI_2, _VALID (and
optionally _PAULI_TOKEN_RE) accordingly.
In @tests/analog/test_analog_tjm.py:
- Around line 57-58: The call to MPO.ising(L, J, g) constructs an MPO but its
return value is unused; either remove this dead statement or assign the result
to a variable (e.g., m = MPO.ising(L, J, g)) and use that variable in subsequent
assertions; update references in the test to use the assigned MPO if the test
intended to validate properties of the constructed MPO.
In @tests/test_simulator.py:
- Around line 462-463: The call to the classmethod MPO.ising is discarding its
returned MPO and leaves H_0 uninitialized; change the call to capture and assign
the returned MPO back to H_0 (e.g., call MPO.ising(L, J, g) or H_0.ising(L, J,
g) and assign the result to H_0) so H_0 holds the new initialized MPO instance.
- Around line 572-573: The call to MPO.ising(L, J, g) is a classmethod that
returns an MPO, but its return value is being discarded; replace the two lines
with a single assignment that captures the returned MPO (e.g., H_0 =
MPO.ising(L, J, g)) so H_0 holds the constructed operator.
- Around line 686-687: The call to the classmethod ising is discarding its
returned MPO; change the test to capture the returned MPO by assigning the
result of MPO.ising(L, J, g) (or H_0 = MPO.ising(L, J, g)) instead of calling
H_0.ising(L, J, g) on the instance; update the reference in
tests/test_simulator.py so H_0 holds the MPO returned by the ising classmethod.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (7)
docs/examples/analog_simulation.mdsrc/mqt/yaqs/core/data_structures/networks.pytests/analog/test_analog_tjm.pytests/core/data_structures/test_networks.pytests/core/methods/test_bug.pytests/core/methods/test_tdvp.pytests/test_simulator.py
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2026-01-08T10:07:23.393Z
Learnt from: flowerthrower
Repo: munich-quantum-toolkit/core-plugins-catalyst PR: 20
File: python/mqt/core/plugins/catalyst/device.py:69-69
Timestamp: 2026-01-08T10:07:23.393Z
Learning: In the munich-quantum-toolkit/core-plugins-catalyst repository, Ruff is configured with select = ["ALL"] and only specific rules are disabled. SLF001 (flake8-self - private member access) is enabled, so `# noqa: SLF001` directives are necessary when accessing private attributes like `device._to_matrix_ops` in python/mqt/core/plugins/catalyst/device.py, even if Ruff reports RUF100 warnings suggesting the directive is unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
📚 Learning: 2025-11-27T21:26:39.677Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/qmap PR: 846
File: python/mqt/qmap/plugins/qiskit/sc/load_calibration.py:34-34
Timestamp: 2025-11-27T21:26:39.677Z
Learning: In the qmap project, the Ruff linter has the "PL" (pylint) rule category enabled, which includes PLC0415 (import-outside-top-level). Therefore, `# noqa: PLC0415` directives on lazy imports are appropriate and necessary, not unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
📚 Learning: 2025-11-24T10:19:41.147Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1326
File: python/mqt/core/__init__.py:22-22
Timestamp: 2025-11-24T10:19:41.147Z
Learning: In the munich-quantum-toolkit/core repository, Ruff is configured with 'ALL' rules enabled by default, and only specific rules are selectively disabled. When reviewing changes that enable previously-disabled rules (like PLC0415), noqa directives for those rules become necessary and should be retained.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
📚 Learning: 2025-12-14T15:23:54.712Z
Learnt from: flowerthrower
Repo: munich-quantum-toolkit/core-plugins-catalyst PR: 23
File: docs/conf.py:110-130
Timestamp: 2025-12-14T15:23:54.712Z
Learning: In the munich-quantum-toolkit/core-plugins-catalyst repository, the Ruff configuration has 'ALL' rules enabled with only specific rules disabled. PLR6301 (no-self-use) is active, so `# noqa: PLR6301` directives are necessary for methods that don't use self, even if Ruff reports RUF100 warnings suggesting the directive is unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
📚 Learning: 2025-12-25T13:28:25.619Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/predictor PR: 526
File: src/mqt/predictor/rl/predictorenv.py:271-271
Timestamp: 2025-12-25T13:28:25.619Z
Learning: In the munich-quantum-toolkit/predictor repository, Ruff is configured with an extensive set of rules including SLF (flake8-self) in the extend-select list. The `# noqa: SLF001` directive for private member access (e.g., `self.state._layout = self.layout` in src/mqt/predictor/rl/predictorenv.py) is necessary and appropriate, even if Ruff reports RUF100 warnings suggesting the directive is unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
📚 Learning: 2025-12-25T13:28:10.402Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/predictor PR: 526
File: tests/compilation/test_predictor_rl.py:225-225
Timestamp: 2025-12-25T13:28:10.402Z
Learning: In the munich-quantum-toolkit/predictor repository, Ruff is configured with the "SLF" (flake8-self) rule category enabled in extend-select. SLF001 (private member access) is active, so `# noqa: SLF001` directives are necessary when accessing private methods/attributes (e.g., `_ensure_device_averages_cached()`), even if Ruff reports RUF100 warnings suggesting the directive is unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
🧬 Code graph analysis (5)
tests/core/methods/test_bug.py (1)
src/mqt/yaqs/core/data_structures/networks.py (3)
custom(1294-1314)MPO(957-1617)ising(1054-1090)
src/mqt/yaqs/core/data_structures/networks.py (1)
src/mqt/yaqs/core/libraries/gate_library.py (5)
Destroy(690-714)x(286-292)h(313-319)U(949-983)conj(269-275)
tests/core/methods/test_tdvp.py (1)
src/mqt/yaqs/core/data_structures/networks.py (2)
MPO(957-1617)ising(1054-1090)
tests/test_simulator.py (1)
src/mqt/yaqs/core/data_structures/networks.py (2)
MPO(957-1617)ising(1054-1090)
tests/analog/test_analog_tjm.py (1)
src/mqt/yaqs/core/data_structures/networks.py (2)
MPO(957-1617)ising(1054-1090)
🪛 Ruff (0.14.10)
src/mqt/yaqs/core/data_structures/networks.py
990-995: Mutable class attributes should be annotated with typing.ClassVar
(RUF012)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
- GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
- GitHub Check: 🐍 Test (macos-14) / 🐍 macos-14
- GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
- GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
🔇 Additional comments (36)
docs/examples/analog_simulation.md (1)
30-30: LGTM!The documentation correctly demonstrates the new
MPO.ising(L, J, g)class method API, which aligns with the broader refactor replacing instance-based initializers with factory-style constructors.tests/core/methods/test_bug.py (5)
83-85: LGTM!The test correctly uses the new
mpo.custom(tensors, transpose=False)API to create a random MPO.
98-99: LGTM!Correctly updated to use the new
mpo.custom([mpo_tensor])API.
122-123: LGTM!Correctly updated to use the new
mpo.custom(mpo_tensors, transpose=False)API.
255-255: LGTM!Correctly migrated to the factory-style
MPO.ising(1, 1, 0.5)class method.
272-272: LGTM!Correctly migrated to the factory-style
MPO.ising(3, 1, 0.5)class method.tests/analog/test_analog_tjm.py (2)
90-91: LGTM!Correctly migrated to
MPO.ising(L, J, g)and assigned toH.
124-125: LGTM!All remaining test functions correctly use the new
MPO.ising(...)factory method with proper variable assignment.Also applies to: 154-155, 184-185, 214-215, 255-256, 381-382
tests/core/methods/test_tdvp.py (4)
245-246: LGTM!Correctly migrated to
MPO.ising(L, J, g)class method.
274-275: LGTM!Correctly migrated to
MPO.ising(L, J, g)class method.
315-315: LGTM!Correctly migrated to
MPO.ising(L, J, g)class method.
348-348: LGTM!Correctly migrated to
MPO.ising(L, J, g)class method.tests/test_simulator.py (3)
77-77: LGTM!Correctly uses the factory-style
MPO.ising(length, J=1, g=0.5)class method.
130-130: LGTM!Correctly uses the factory-style
MPO.ising(length, J=1, g=0.5)class method.
179-179: LGTM!Correctly uses the factory-style
MPO.ising(length, J=1, g=0.5)class method.tests/core/data_structures/test_networks.py (10)
38-38: LGTM!Import updated to include
GateLibraryandIdfor test usage.
104-160: LGTM!The dense Hamiltonian helper functions (
_embed_one_body,_embed_two_body,_ising_dense,_heisenberg_dense) correctly implement reference dense matrix construction for verifying MPO correctness. These are well-structured and will serve as ground truth for MPO validation tests.
163-183: Excellent addition of correctness verification tests.
test_ising_correct_operatorandtest_heisenberg_correct_operatorvalidate the new Pauli-sum MPO construction against analytically-constructed dense Hamiltonians. This is a strong verification approach that ensures the MPOto_matrix()representation matches the expected physics.
186-196: LGTM!Test correctly migrated to use
mpo.identity(length, physical_dimension=pdim).
207-235: LGTM!Test correctly migrated to use
mpo.finite_state_machine(length, left_bound, inner, right_bound).
238-261: LGTM!Test correctly migrated to use
mpo.custom(tensors).
264-285: LGTM!Test correctly migrated to use
MPO.ising(length, J, g)classmethod.
288-297: LGTM!Test correctly migrated to use
MPO.ising(length, J, g)classmethod.
300-319: LGTM!Test correctly migrated to use
MPO.ising(length, J, g)classmethod.
322-334: LGTM!Test correctly migrated to use
mpo.identity(length, pdim).src/mqt/yaqs/core/data_structures/networks.py (11)
1053-1090: LGTM!The
isingclassmethod cleanly delegates tohamiltonianwith appropriate two-body (-J * Z⊗Z) and one-body (-g * X) terms. The docstring is comprehensive and matches the implementation.
1092-1133: LGTM!The
heisenbergclassmethod correctly constructs an XYZ Heisenberg Hamiltonian with optional Z field. The conditionalone_body=[(-h, "Z")] if h != 0 else []appropriately handles the case when no field is applied.
1250-1265: LGTM!The
identitymethod is renamed frominit_identityand maintains the same functionality.
1267-1292: LGTM!The
finite_state_machinemethod is renamed frominit_custom_hamiltonianand maintains the same functionality for constructing MPOs from explicit boundary and inner tensors.
1294-1314: LGTM!The
custommethod is renamed frominit_customand maintains the same functionality with improved keyword-only argument pattern fortranspose.
1316-1363: LGTM!The
from_pauli_summethod correctly:
- Validates physical dimension and length
- Parses Pauli string specifications
- Creates individual term MPOs
- Sums terms via block-diagonal concatenation
- Compresses the result
The implementation follows the algorithm from Hubig et al. as mentioned in the PR objectives.
1365-1408: LGTM!The
compressmethod provides a clean public API for MPO compression with flexible sweep direction control. Input validation is thorough and the schedule mapping is clear.
1410-1459: LGTM!The
_compress_one_sweepmethod correctly implements local SVD-based compression:
- Contracts adjacent tensors
- Performs SVD with truncation based on tolerance and max_bond_dim
- Reconstructs left and right tensors with proper index ordering
The index manipulation and reshape operations correctly preserve the MPO structure.
1560-1571: LGTM!The
_create_termhelper correctly constructs a single Pauli term MPO with bond dimension 1 throughout, applying the coefficient to the first tensor.
1573-1590: LGTM!The
_parse_pauli_stringmethod provides robust parsing of Pauli string specifications with:
- Support for comma or space-separated tokens
- Case-insensitive matching
- Duplicate site detection
- Validation of remaining tokens after regex matching
1592-1617: LGTM!The
_sum_termsstatic method correctly implements MPO addition via block-diagonal concatenation of virtual bonds. The boundary handling for first/last sites is correct, and the inner site concatenation properly forms block-diagonal structure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/mqt/yaqs/core/data_structures/networks.py (1)
1254-1318: API inconsistency: mix of classmethods and instance methods.
ising(),heisenberg(),coupled_transmon(), andhamiltonian()are classmethods that return a newMPOinstance, whileidentity(),finite_state_machine(), andcustom()are instance methods that mutateselfin-place. This inconsistency can confuse users:# Classmethod pattern (returns new instance) mpo = MPO.ising(L=10, J=1, g=0.5) # Instance method pattern (mutates in-place) mpo = MPO() mpo.identity(length=10)Consider refactoring
identity(),finite_state_machine(), andcustom()to classmethods for a consistent API surface.
🤖 Fix all issues with AI agents
In @src/mqt/yaqs/core/data_structures/networks.py:
- Around line 990-997: The class-level mutable/static attributes _PAULI_2,
_VALID, and _PAULI_TOKEN_RE should be annotated with typing.ClassVar to satisfy
static analysis; import ClassVar (and re.Pattern if you want precise type for
the regex) unconditionally at top-level and change their annotations to
something like _PAULI_2: ClassVar[dict[str, np.ndarray]] = {...}, _VALID:
ClassVar[frozenset[str]] = frozenset(...), and _PAULI_TOKEN_RE:
ClassVar[re.Pattern] = re.compile(...). Ensure the import of ClassVar is added
(not only under TYPE_CHECKING) so annotations exist at runtime.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (3)
docs/examples/transmon_emulation.mdsrc/mqt/yaqs/core/data_structures/networks.pytests/test_simulator.py
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2026-01-08T10:07:23.393Z
Learnt from: flowerthrower
Repo: munich-quantum-toolkit/core-plugins-catalyst PR: 20
File: python/mqt/core/plugins/catalyst/device.py:69-69
Timestamp: 2026-01-08T10:07:23.393Z
Learning: In the munich-quantum-toolkit/core-plugins-catalyst repository, Ruff is configured with select = ["ALL"] and only specific rules are disabled. SLF001 (flake8-self - private member access) is enabled, so `# noqa: SLF001` directives are necessary when accessing private attributes like `device._to_matrix_ops` in python/mqt/core/plugins/catalyst/device.py, even if Ruff reports RUF100 warnings suggesting the directive is unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
📚 Learning: 2025-11-27T21:26:39.677Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/qmap PR: 846
File: python/mqt/qmap/plugins/qiskit/sc/load_calibration.py:34-34
Timestamp: 2025-11-27T21:26:39.677Z
Learning: In the qmap project, the Ruff linter has the "PL" (pylint) rule category enabled, which includes PLC0415 (import-outside-top-level). Therefore, `# noqa: PLC0415` directives on lazy imports are appropriate and necessary, not unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
📚 Learning: 2025-11-24T10:19:41.147Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1326
File: python/mqt/core/__init__.py:22-22
Timestamp: 2025-11-24T10:19:41.147Z
Learning: In the munich-quantum-toolkit/core repository, Ruff is configured with 'ALL' rules enabled by default, and only specific rules are selectively disabled. When reviewing changes that enable previously-disabled rules (like PLC0415), noqa directives for those rules become necessary and should be retained.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
📚 Learning: 2025-12-14T15:23:54.712Z
Learnt from: flowerthrower
Repo: munich-quantum-toolkit/core-plugins-catalyst PR: 23
File: docs/conf.py:110-130
Timestamp: 2025-12-14T15:23:54.712Z
Learning: In the munich-quantum-toolkit/core-plugins-catalyst repository, the Ruff configuration has 'ALL' rules enabled with only specific rules disabled. PLR6301 (no-self-use) is active, so `# noqa: PLR6301` directives are necessary for methods that don't use self, even if Ruff reports RUF100 warnings suggesting the directive is unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
📚 Learning: 2025-12-25T13:28:25.619Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/predictor PR: 526
File: src/mqt/predictor/rl/predictorenv.py:271-271
Timestamp: 2025-12-25T13:28:25.619Z
Learning: In the munich-quantum-toolkit/predictor repository, Ruff is configured with an extensive set of rules including SLF (flake8-self) in the extend-select list. The `# noqa: SLF001` directive for private member access (e.g., `self.state._layout = self.layout` in src/mqt/predictor/rl/predictorenv.py) is necessary and appropriate, even if Ruff reports RUF100 warnings suggesting the directive is unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
📚 Learning: 2025-12-25T13:28:10.402Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/predictor PR: 526
File: tests/compilation/test_predictor_rl.py:225-225
Timestamp: 2025-12-25T13:28:10.402Z
Learning: In the munich-quantum-toolkit/predictor repository, Ruff is configured with the "SLF" (flake8-self) rule category enabled in extend-select. SLF001 (private member access) is active, so `# noqa: SLF001` directives are necessary when accessing private methods/attributes (e.g., `_ensure_device_averages_cached()`), even if Ruff reports RUF100 warnings suggesting the directive is unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
🧬 Code graph analysis (1)
tests/test_simulator.py (1)
src/mqt/yaqs/core/data_structures/networks.py (3)
MPO(957-1621)ising(1054-1090)coupled_transmon(1136-1252)
🪛 Ruff (0.14.10)
src/mqt/yaqs/core/data_structures/networks.py
990-995: Mutable class attributes should be annotated with typing.ClassVar
(RUF012)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
- GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
- GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
- GitHub Check: 🐍 Test (macos-14) / 🐍 macos-14
- GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
🔇 Additional comments (18)
docs/examples/transmon_emulation.md (1)
35-42: LGTM!The documentation correctly demonstrates the updated API using the classmethod constructor
MPO.coupled_transmon(...)instead of the previous instance-based initialization pattern.tests/test_simulator.py (7)
77-77: LGTM!The test correctly uses the new
MPO.ising()classmethod constructor with appropriate parameters.
130-130: LGTM!Consistent migration to
MPO.ising()classmethod.
179-179: LGTM!Consistent migration to
MPO.ising()classmethod.
462-462: LGTM!Consistent migration to
MPO.ising()classmethod.
571-571: LGTM!Consistent migration to
MPO.ising()classmethod.
684-684: LGTM!Consistent migration to
MPO.ising()classmethod.
841-849: LGTM!The test correctly uses the new
MPO.coupled_transmon()classmethod constructor, and the parameter names match the new API signature.src/mqt/yaqs/core/data_structures/networks.py (10)
999-1051: LGTM!The
hamiltonian()classmethod correctly:
- Validates inputs (length > 0, valid boundary conditions)
- Builds term specifications for both two-body and one-body interactions
- Properly handles periodic boundary conditions with modulo arithmetic
- Delegates to
from_pauli_sum()for MPO construction
1053-1133: LGTM!The
ising()andheisenberg()classmethods correctly delegate tohamiltonian()with proper term specifications:
- Ising:
$-J \sum Z_i Z_{i+1} - g \sum X_i$ - Heisenberg:
$-J_x \sum X_i X_{i+1} - J_y \sum Y_i Y_{i+1} - J_z \sum Z_i Z_{i+1} - h \sum Z_i$ The conditional
one_bodyforh != 0in Heisenberg avoids unnecessary identity terms.
1135-1252: LGTM!The
coupled_transmon()classmethod correctly implements the transmon-resonator chain Hamiltonian:
- Proper tensor structure for boundary and bulk sites
- Correct physical dimension handling for alternating qubits/resonators
- Appropriate transpose to match MPO index convention
(phys_out, phys_in, left, right)
1358-1364: Verify: zero MPO for empty terms list.When
termsis empty, the method creates a zero MPO (tensors filled with zeros). This produces an MPO representing the zero operator, which may not be the expected behavior for users who accidentally pass an empty list.Consider either:
- Raising an error for empty terms
- Documenting this behavior in the docstring
1414-1463: LGTM!The
_compress_one_sweep()method correctly implements local SVD compression:
- Proper tensor contraction of adjacent sites
- Correct reshape for SVD (grouping left/right virtual and physical indices)
- Appropriate truncation based on tolerance threshold
- Ensures at least one singular value is kept
- Correct reconstruction of left/right tensors with proper transposition
1577-1594: LGTM!The
_parse_pauli_string()method correctly parses Pauli term specifications with proper validation for duplicate sites and invalid tokens. The regex pattern appropriately handles various input formats.
1596-1620: LGTM!The
_sum_terms()method correctly implements MPO summation via bond dimension expansion using block-diagonal concatenation. The special cases for boundary tensors (first and last sites) are handled correctly.
1564-1575: LGTM!The
_create_term()method correctly creates a bond-dimension-1 MPO for a single Pauli string term, with proper coefficient application and identity filling for unspecified sites.
1554-1555: LGTM!The usage of
identity_mpo.identity(self.length)is consistent withidentity()being an instance method.
1465-1481: LGTM!The
rotate()method correctly implements MPO rotation by swapping physical dimensions. The keyword-onlyconjugateargument is a good API choice for boolean flags.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/mqt/yaqs/core/data_structures/networks.py (1)
1291-1308: Bug:identity()ignoresphysical_dimensionparameter.The identity matrix is hardcoded as 2×2, ignoring the
physical_dimensionparameter. This will produce incorrect results for non-qubit systems (e.g., qutrits).🐛 Proposed fix
def identity(self, length: int, physical_dimension: int = 2) -> None: """Initialize identity MPO. Initializes the network with identity matrices. Parameters: length (int): The number of identity matrices to initialize. physical_dimension (int, optional): The physical dimension of the identity matrices. Default is 2. """ - mat = np.eye(2, dtype=np.complex128) + mat = np.eye(physical_dimension, dtype=np.complex128) mat = np.expand_dims(mat, (2, 3)) self.length = length self.physical_dimension = physical_dimension self.tensors = [] for _ in range(length): self.tensors.append(mat)
🤖 Fix all issues with AI agents
In @src/mqt/yaqs/core/data_structures/networks.py:
- Line 1091: Remove the unused noqa directive on the parameter declaration:
locate the parameter "J: float, # noqa: N803" in the affected function/class in
networks.py and delete the " # noqa: N803" suffix so the parameter is simply "J:
float,"; no other changes needed.
- Around line 1130-1132: Remove the unused inline noqa directives from the
parameter names: delete the trailing " # noqa: N803" on the Jx, Jy, and Jz
parameters so the signature simply uses Jx: float, Jy: float, Jz: float (locate
the occurrence in the function/method where Jx, Jy, Jz are declared and remove
the "# noqa: N803" tokens).
In @tests/core/data_structures/test_networks.py:
- Line 47: Remove the unused "# noqa: N803" directives from the function
definitions that currently include them: _embed_one_body, _embed_two_body, and
_embed_three_body; simply delete the trailing "# noqa: N803" from their def
lines so the function signatures remain unchanged but the stale noqa comments
are removed.
- Around line 113-139: Remove the unused lint suppression from the function
definition: edit the _heisenberg_dense function signature to delete the "# noqa:
N803" comment so the line reads "def _heisenberg_dense(L: int, Jx: float, Jy:
float, Jz: float, h: float) -> np.ndarray:"; leave all logic unchanged.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (2)
src/mqt/yaqs/core/data_structures/networks.pytests/core/data_structures/test_networks.py
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: burgholzer
Repo: munich-quantum-toolkit/yaqs PR: 212
File: CHANGELOG.md:12-15
Timestamp: 2025-10-14T14:37:38.047Z
Learning: In the munich-quantum-toolkit/yaqs project, changelog entries follow the template: "- $TITLE ([#$NUMBER]($URL)) ([**AUTHOR**](https://github.com/$AUTHOR))". Issue references should not be included in changelog entries; the PR number is sufficient for traceability.
📚 Learning: 2026-01-08T10:07:23.393Z
Learnt from: flowerthrower
Repo: munich-quantum-toolkit/core-plugins-catalyst PR: 20
File: python/mqt/core/plugins/catalyst/device.py:69-69
Timestamp: 2026-01-08T10:07:23.393Z
Learning: In the munich-quantum-toolkit/core-plugins-catalyst repository, Ruff is configured with select = ["ALL"] and only specific rules are disabled. SLF001 (flake8-self - private member access) is enabled, so `# noqa: SLF001` directives are necessary when accessing private attributes like `device._to_matrix_ops` in python/mqt/core/plugins/catalyst/device.py, even if Ruff reports RUF100 warnings suggesting the directive is unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
📚 Learning: 2025-11-27T21:26:39.677Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/qmap PR: 846
File: python/mqt/qmap/plugins/qiskit/sc/load_calibration.py:34-34
Timestamp: 2025-11-27T21:26:39.677Z
Learning: In the qmap project, the Ruff linter has the "PL" (pylint) rule category enabled, which includes PLC0415 (import-outside-top-level). Therefore, `# noqa: PLC0415` directives on lazy imports are appropriate and necessary, not unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
📚 Learning: 2025-11-24T10:19:41.147Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1326
File: python/mqt/core/__init__.py:22-22
Timestamp: 2025-11-24T10:19:41.147Z
Learning: In the munich-quantum-toolkit/core repository, Ruff is configured with 'ALL' rules enabled by default, and only specific rules are selectively disabled. When reviewing changes that enable previously-disabled rules (like PLC0415), noqa directives for those rules become necessary and should be retained.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
📚 Learning: 2025-12-14T15:23:54.712Z
Learnt from: flowerthrower
Repo: munich-quantum-toolkit/core-plugins-catalyst PR: 23
File: docs/conf.py:110-130
Timestamp: 2025-12-14T15:23:54.712Z
Learning: In the munich-quantum-toolkit/core-plugins-catalyst repository, the Ruff configuration has 'ALL' rules enabled with only specific rules disabled. PLR6301 (no-self-use) is active, so `# noqa: PLR6301` directives are necessary for methods that don't use self, even if Ruff reports RUF100 warnings suggesting the directive is unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
📚 Learning: 2025-12-25T13:28:25.619Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/predictor PR: 526
File: src/mqt/predictor/rl/predictorenv.py:271-271
Timestamp: 2025-12-25T13:28:25.619Z
Learning: In the munich-quantum-toolkit/predictor repository, Ruff is configured with an extensive set of rules including SLF (flake8-self) in the extend-select list. The `# noqa: SLF001` directive for private member access (e.g., `self.state._layout = self.layout` in src/mqt/predictor/rl/predictorenv.py) is necessary and appropriate, even if Ruff reports RUF100 warnings suggesting the directive is unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
📚 Learning: 2025-12-25T13:28:10.402Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/predictor PR: 526
File: tests/compilation/test_predictor_rl.py:225-225
Timestamp: 2025-12-25T13:28:10.402Z
Learning: In the munich-quantum-toolkit/predictor repository, Ruff is configured with the "SLF" (flake8-self) rule category enabled in extend-select. SLF001 (private member access) is active, so `# noqa: SLF001` directives are necessary when accessing private methods/attributes (e.g., `_ensure_device_averages_cached()`), even if Ruff reports RUF100 warnings suggesting the directive is unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
🧬 Code graph analysis (1)
tests/core/data_structures/test_networks.py (2)
src/mqt/yaqs/core/libraries/gate_library.py (6)
GateLibrary(1630-1724)Id(744-763)X(602-621)Z(646-665)H(668-687)h(313-319)src/mqt/yaqs/core/data_structures/networks.py (8)
op(1056-1061)MPO(957-1749)ising(1088-1124)to_matrix(1568-1590)heisenberg(1127-1167)identity(1291-1308)finite_state_machine(1310-1335)custom(1337-1357)
🪛 Ruff (0.14.10)
tests/core/data_structures/test_networks.py
47-47: Unused noqa directive (non-enabled: N803)
Remove unused noqa directive
(RUF100)
64-64: Unused noqa directive (non-enabled: N803)
Remove unused noqa directive
(RUF100)
88-88: Unused noqa directive (non-enabled: N803)
Remove unused noqa directive
(RUF100)
113-113: Unused noqa directive (non-enabled: N803)
Remove unused noqa directive
(RUF100)
src/mqt/yaqs/core/data_structures/networks.py
1091-1091: Unused noqa directive (non-enabled: N803)
Remove unused noqa directive
(RUF100)
1130-1130: Unused noqa directive (non-enabled: N803)
Remove unused noqa directive
(RUF100)
1131-1131: Unused noqa directive (non-enabled: N803)
Remove unused noqa directive
(RUF100)
1132-1132: Unused noqa directive (non-enabled: N803)
Remove unused noqa directive
(RUF100)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
- GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
- GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
- GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
- GitHub Check: 🐍 Test (macos-14) / 🐍 macos-14
🔇 Additional comments (13)
tests/core/data_structures/test_networks.py (4)
38-45: LGTM!Defining local Pauli matrices for test verification is appropriate. This isolates tests from the production
GateLibraryimplementation when building reference dense Hamiltonians.
205-228: Excellent correctness tests for the new MPO builders.These tests properly validate that the MPO representation matches the exact dense Hamiltonian. Using
L=5keeps the dense matrix tractable (32×32) while still exercising the construction logic across multiple sites.
230-306: LGTM!The tests correctly exercise the builder methods (
identity,finite_state_machine,custom) using the instance-method pattern and thoroughly validate tensor shapes and values.
308-378: LGTM!Tests are properly updated to use the new
MPO.ising(...)classmethod and continue to validate the core MPO operations (conversion, validation, rotation, identity check).src/mqt/yaqs/core/data_structures/networks.py (9)
21-22: LGTM!Appropriate imports for regex-based Pauli string parsing and proper type annotations.
995-1006: LGTM!The class variables are well-defined. The regex pattern correctly handles case-insensitive Pauli labels with optional whitespace before the site index.
1008-1085: LGTM!The
hamiltonian()classmethod correctly generates Pauli-string terms for both open and periodic boundary conditions. The validation and delegation tofrom_pauli_sum()is clean.
1169-1289: LGTM!The refactoring to a classmethod is clean. The tensor construction correctly implements the coupled transmon-resonator Hamiltonian with proper MPO conventions.
1359-1423: LGTM!The
from_pauli_sum()method correctly assembles MPO terms from Pauli string specifications with proper validation of sites and operators. The handling of empty terms (creating a zero operator) is mathematically sound.
1425-1530: LGTM!The compression implementation is correct. The SVD-based sweep with configurable directions and tolerance is well-implemented, and the minimum rank of 1 ensures numerical stability.
1532-1548: LGTM!The
rotate()method correctly handles physical dimension transposition with optional conjugation.
1631-1662: LGTM!The
_create_term()method correctly constructs a bond-dimension-1 MPO for a single Pauli string term. Applying the coefficient to the first tensor is the standard convention.
1664-1748: LGTM!Both helper methods are well-implemented:
_parse_pauli_string()provides robust parsing with duplicate site detection and validation of malformed tokens._sum_terms()correctly implements block-diagonal concatenation for MPO addition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/mqt/yaqs/core/data_structures/networks.py (1)
1297-1363: Consider API consistency: classmethods vs. instance methods.The builder methods
identity,finite_state_machine, andcustomare implemented as in-place instance methods (returningNone), whileising,heisenberg,hamiltonian, andcoupled_transmonare classmethods that return newMPOinstances.This creates two different usage patterns:
# Classmethod pattern (newer, preferred?) mpo1 = MPO.ising(length=10, J=1.0, g=0.5) # Instance method pattern (legacy?) mpo2 = MPO() mpo2.identity(length=10)Suggested improvement: Consider converting
identity,finite_state_machine, andcustomto classmethods for consistency, or document the rationale for the different patterns.
🤖 Fix all issues with AI agents
In @src/mqt/yaqs/simulator.py:
- Line 150: Wrap the fallback chain that returns os.cpu_count() or
multiprocessing.cpu_count() or 1 in a try/except so multiprocessing.cpu_count()
cannot raise out unhandled (e.g., NotImplementedError); catch
NotImplementedError (and a broad Exception as a safeguard), and on exception
return 1 or use os.cpu_count() result if available, ensuring the expression
using os.cpu_count() and multiprocessing.cpu_count() is evaluated safely.
- Around line 142-147: The attribute-check refactor removed the try/except
around os.sched_getaffinity(0), so when calling sched_getaffinity (via fn =
getattr(os, "sched_getaffinity", None) and sched_getaffinity = cast(...)) the
OSError can bubble up; wrap the call to sched_getaffinity(0) in a try/except
OSError (and OSError subclasses) and on exception fall through to the existing
fallback logic (the subsequent CPU-count fallback) instead of raising; preserve
the current behavior of returning len(result) when the call succeeds.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (2)
src/mqt/yaqs/core/data_structures/networks.pysrc/mqt/yaqs/simulator.py
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2026-01-08T10:07:23.393Z
Learnt from: flowerthrower
Repo: munich-quantum-toolkit/core-plugins-catalyst PR: 20
File: python/mqt/core/plugins/catalyst/device.py:69-69
Timestamp: 2026-01-08T10:07:23.393Z
Learning: In the munich-quantum-toolkit/core-plugins-catalyst repository, Ruff is configured with select = ["ALL"] and only specific rules are disabled. SLF001 (flake8-self - private member access) is enabled, so `# noqa: SLF001` directives are necessary when accessing private attributes like `device._to_matrix_ops` in python/mqt/core/plugins/catalyst/device.py, even if Ruff reports RUF100 warnings suggesting the directive is unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
📚 Learning: 2025-11-27T21:26:39.677Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/qmap PR: 846
File: python/mqt/qmap/plugins/qiskit/sc/load_calibration.py:34-34
Timestamp: 2025-11-27T21:26:39.677Z
Learning: In the qmap project, the Ruff linter has the "PL" (pylint) rule category enabled, which includes PLC0415 (import-outside-top-level). Therefore, `# noqa: PLC0415` directives on lazy imports are appropriate and necessary, not unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
📚 Learning: 2025-11-24T10:19:41.147Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1326
File: python/mqt/core/__init__.py:22-22
Timestamp: 2025-11-24T10:19:41.147Z
Learning: In the munich-quantum-toolkit/core repository, Ruff is configured with 'ALL' rules enabled by default, and only specific rules are selectively disabled. When reviewing changes that enable previously-disabled rules (like PLC0415), noqa directives for those rules become necessary and should be retained.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
📚 Learning: 2025-12-14T15:23:54.712Z
Learnt from: flowerthrower
Repo: munich-quantum-toolkit/core-plugins-catalyst PR: 23
File: docs/conf.py:110-130
Timestamp: 2025-12-14T15:23:54.712Z
Learning: In the munich-quantum-toolkit/core-plugins-catalyst repository, the Ruff configuration has 'ALL' rules enabled with only specific rules disabled. PLR6301 (no-self-use) is active, so `# noqa: PLR6301` directives are necessary for methods that don't use self, even if Ruff reports RUF100 warnings suggesting the directive is unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
📚 Learning: 2025-12-25T13:28:25.619Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/predictor PR: 526
File: src/mqt/predictor/rl/predictorenv.py:271-271
Timestamp: 2025-12-25T13:28:25.619Z
Learning: In the munich-quantum-toolkit/predictor repository, Ruff is configured with an extensive set of rules including SLF (flake8-self) in the extend-select list. The `# noqa: SLF001` directive for private member access (e.g., `self.state._layout = self.layout` in src/mqt/predictor/rl/predictorenv.py) is necessary and appropriate, even if Ruff reports RUF100 warnings suggesting the directive is unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
📚 Learning: 2025-12-25T13:28:10.402Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/predictor PR: 526
File: tests/compilation/test_predictor_rl.py:225-225
Timestamp: 2025-12-25T13:28:10.402Z
Learning: In the munich-quantum-toolkit/predictor repository, Ruff is configured with the "SLF" (flake8-self) rule category enabled in extend-select. SLF001 (private member access) is active, so `# noqa: SLF001` directives are necessary when accessing private methods/attributes (e.g., `_ensure_device_averages_cached()`), even if Ruff reports RUF100 warnings suggesting the directive is unused.
Applied to files:
src/mqt/yaqs/core/data_structures/networks.py
🧬 Code graph analysis (1)
src/mqt/yaqs/core/data_structures/networks.py (1)
src/mqt/yaqs/core/libraries/gate_library.py (5)
x(286-292)h(313-319)u(412-421)conj(269-275)p(400-409)
🪛 Ruff (0.14.10)
src/mqt/yaqs/core/data_structures/networks.py
1097-1097: Unused noqa directive (non-enabled: N803)
Remove unused noqa directive
(RUF100)
1136-1136: Unused noqa directive (non-enabled: N803)
Remove unused noqa directive
(RUF100)
1137-1137: Unused noqa directive (non-enabled: N803)
Remove unused noqa directive
(RUF100)
1138-1138: Unused noqa directive (non-enabled: N803)
Remove unused noqa directive
(RUF100)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
- GitHub Check: 🐍 Test (macos-14) / 🐍 macos-14
- GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
- GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
- GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
- GitHub Check: 🐍 Lint / 🚨 Lint
🔇 Additional comments (14)
src/mqt/yaqs/simulator.py (2)
57-60: LGTM! Good typing hygiene.The addition of
castand guardingCallableunderTYPE_CHECKINGis a best practice that reduces runtime overhead while maintaining type safety.
96-96: LGTM! Consistent with the import reorganization.Correctly removed the duplicate
Callableimport since it's now imported at the top-level TYPE_CHECKING block.src/mqt/yaqs/core/data_structures/networks.py (12)
21-21: LGTM: Clean import additions and type alias.The new imports (
re,NDArray,Destroy) and theComplexTensortype alias support the Pauli-string parsing and MPO construction features. The type alias improves code readability.Also applies to: 26-26, 29-29, 956-956
997-1008: LGTM: Well-defined Pauli constants and parsing pattern.The class-level constants are properly defined:
_PAULI_2contains correct 2×2 Pauli matrices_VALIDensures type safety for operator validation_PAULI_TOKEN_REcorrectly parses compact Pauli-string notation like "X0 Y2 Z5"Using
ClassVarappropriately marks these as class-level attributes.
1014-1091: LGTM: Solid API for Hamiltonian construction.The
hamiltonianclassmethod provides a clean interface for building MPOs from one- and two-body Pauli interactions. The implementation correctly:
- Validates inputs (length, boundary conditions, operator labels)
- Handles open vs. periodic boundary conditions
- Expands interaction templates to full Pauli-string terms
- Delegates to
from_pauli_sumfor the actual MPO assembly
1175-1295: LGTM: Clean refactor to classmethod with good documentation.The conversion of
coupled_transmonto a classmethod aligns with the new builder pattern. The implementation correctly:
- Constructs alternating qubit/resonator chain tensors
- Handles boundary conditions (first, last, middle sites)
- Documents the backward-compatibility caveat about
physical_dimensionstoring onlyqubit_dimdespite alternating dimensions- Validates the constructed MPO
1365-1429: Solid Pauli-sum MPO builder with one edge case to verify.The
from_pauli_summethod correctly:
- Validates inputs (dimension, length, sites, operators)
- Parses and assembles Pauli-string terms
- Sums terms via block-diagonal bond concatenation
- Compresses the result to reduce bond dimension
Edge case: When
termsis empty (line 1420), the method constructs a zero MPO (all-zero tensors). Verify this is the intended behavior rather than an identity MPO or an error.# Verify zero MPO behavior for empty terms mpo = MPO() mpo.from_pauli_sum(terms=[], length=4) mat = mpo.to_matrix() print("Zero MPO check:", np.allclose(mat, 0)) # Should be True
1431-1536: Excellent MPO compression implementation.The compression methods are well-designed:
- Public
compressAPI provides flexible multi-sweep scheduling- Internal
_compress_one_sweepimplements the SVD logic cleanly- Proper handling of edge cases (empty MPO, single-site, minimum rank)
The SVD truncation correctly:
- Contracts adjacent tensor pairs
- Reshapes to matrix form for SVD
- Keeps singular values where
tol < s(line 1519)- Enforces
keep = max(1, keep)to prevent rank-zero bonds (line 1522)
1538-1554: LGTM: Simple and correct dimension rotation.The
rotatemethod correctly swaps the physical output/input legs of the MPO, with optional complex conjugation for Hermitian transposition.
1637-1668: LGTM: Correct single-term MPO construction.The
_create_termhelper correctly constructs a bond-dimension-1 MPO for a single Pauli-string term:
- Defaults unspecified sites to identity
- Applies the coefficient to the first site tensor
- Produces properly shaped (2, 2, 1, 1) tensors
1670-1710: LGTM: Robust Pauli-string parser.The
_parse_pauli_stringmethod provides robust parsing with good error handling:
- Accepts flexible whitespace/comma-separated notation
- Detects duplicate site specifications
- Validates token format
- Provides clear error messages
1712-1755: LGTM: Correct MPO direct-sum implementation.The
_sum_termsmethod correctly implements block-diagonal concatenation of MPO bond spaces:
- Handles single-site case (direct addition)
- Properly sizes new tensors with enlarged bonds
- Places sub-blocks correctly (top-left and bottom-right)
- Maintains MPO structural validity
1627-1628: LGTM: Updated to use refactored API.Correctly updated to call
identity(...)instead of the legacyinit_identity(...).
1093-1173: Clean Hamiltonian builder methods with correct noqa directives.The
isingandheisenbergclassmethods provide convenient physics-oriented APIs that delegate to the generalhamiltonianbuilder. The implementations are correct and follow standard conventions for these models.The
# noqa: N803directives on lines 1097, 1136-1138 are necessary and correct. The Ruff configuration enables all rules viaselect = ["ALL"]and does not disable N803, so these directives appropriately suppress the invalid-name warning for the physics-convention parameter names (J, g, Jx, Jy, Jz).
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Aaron Sander <[email protected]>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Aaron Sander <[email protected]>
Description
This PR implements a construction method that allows initializing an MPO directly from an arbitrary Hamiltonian expressed as a sum of weighted Pauli tensor product strings. This enables easy construction of complex Hamiltonians (e.g. transverse-field Ising, Fermi–Hubbard) without manually deriving MPOs. The implementation follows the algorithm presented in Hubig et al.
Additionally, it changes the basic usage of Hamiltonian initialization by using constructors:
H = MPO.ising(L, J, g)Fixes #13
Checklist: