Add representation-independent PauliString class#37
Conversation
… enhance LinearCombinationOfPauliStrings initialization
There was a problem hiding this comment.
Pull request overview
This PR adds a representation-independent PauliString value type and updates the Python Hamiltonian implementation to use canonical PauliString keys internally, while preserving existing dense-string and sparse-tuple public APIs.
Changes:
- Introduces
qhat.common.pauli_string.PauliStringwith dense/sparse construction, conversion, validation, and canonicalization. - Refactors
LinearCombinationOfPauliStringsandHamiltonian.get_all_pauli_strings()to store/return canonicalPauliStringobjects (with a newreturn_as="objects"option). - Expands test coverage for
PauliStringbehavior and Hamiltonian integration (including combining equivalent sparse keys).
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| common/pauli_string.py | Adds the new immutable, hashable PauliString class with canonical sparse storage and conversions. |
| common/tests/test_pauli_string.py | Adds unit tests for PauliString construction, conversions, validation, hashing, and canonicalization. |
| analysis/hamiltonian.py | Refactors Pauli conversion helpers and Hamiltonian/LCPS internals to use canonical PauliString keys; adds return_as="objects". |
| analysis/tests/test_pauli_hamiltonian.py | Updates and adds tests for new canonicalization behavior and return_as="objects" API. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| try: | ||
| return PauliString.from_dense(dense_pauli).to_sparse() | ||
| except ValueError as exc: | ||
| raise ValueError(f"Invalid character in dense pauli string: \"{dense_pauli}\".") from exc |
| num_qubits = _validate_num_qubits(num_qubits) | ||
| return cls( | ||
| num_qubits=num_qubits, | ||
| _operators=_normalize_sparse(operators, num_qubits), |
There was a problem hiding this comment.
Nonblocking: from_sparse() validates and normalizes here, then __post_init__() repeats both operations on the resulting tuple. LinearCombinationOfPauliStrings invokes this path for every sparse term, so large Hamiltonian loads do twice the validation and sorting work. Consider letting __post_init__() normalize the raw input once, or adding a private constructor for already-normalized data.
| return self.to_dense() | ||
|
|
||
| def __repr__(self) -> str: | ||
| return f"PauliString.from_dense({self.to_dense()!r})" |
There was a problem hiding this comment.
Nonblocking: These user-facing string representations are new behavior, and dictionaries display PauliString keys through __repr__, but the tests do not assert either method. Add focused assertions for str() and repr() so accidental output changes are caught.
| # -- If return_as == "strings": The Pauli string is encoded as a character string, where | ||
| # each character is a Pauli matrix, explicitly including identity entries. For example, | ||
| # assuming 6 qubits, "XIIZII". | ||
| # -- If return_as == "objects": Keys are immutable PauliString instances that can provide |
There was a problem hiding this comment.
Nonblocking: return_as="objects" and PauliString add public API surface, but neither analysis/README.md nor common/README.md explains how to import, construct, convert, or request these objects. Add a short usage example so users can discover the representation-independent path without reading implementation comments.
AlbertLee125
left a comment
There was a problem hiding this comment.
Review summary
- Blocking issues: None.
- Nonblocking issues: Avoid duplicate sparse normalization; add coverage for the new string representations; document the new PauliString and return_as="objects" API.
- Questions: None.
- Tests run and outcomes: 62 targeted tests passed; the documented common suite passed (171); the documented analysis suite passed (204, with one pre-existing PytestReturnNotNoneWarning); exhaustive dense/sparse round trips through four qubits, pickle, sparse permutation invariance, InteractionOperator conversions, and canonical LCPS output checks passed. All current GitHub Actions jobs also pass.
- Merge recommendation: Merge-ready; the inline items are nonblocking. GitHub does not allow the PR author account to submit APPROVE.
Summary
This PR introduces a representation-independent
PauliStringclass and integrates it into the Python Hamiltonian implementation.Changes to
common/pauli_string.pyPauliStringvalue type.strandreproutput.Changes to
analysis/hamiltonian.pyPauliString.LinearCombinationOfPauliStringsto store canonicalPauliStringkeys internally.PauliString.return_as="objects"toHamiltonian.get_all_pauli_strings().Issue
Closes #36: Create Pauli string class.
This centralizes Pauli-string conversion and validation while reducing the need for callers to track the active representation manually.
Testing