Open
Conversation
## Major Update Summary
- Unified circuit architecture to support both unitary and measurement operations, and more flexible circuit class to compose any type of operation.
- Unified measurement and post-selection framework to calculate state projection and measurement probability, using log2 scale to handle all probabilities.
- Matrix representation of states and operators moves away form QuTiP dependency in favor of pure NumPy implementations.
- Caching mechanisms for both measurement outcomes and random unitaries to support classical shadow collection via systematic forward/backward passes through the circuit.
## File-by-File Updates
### `__init__.py`
- All imports now reference new class and function names
- new state construction function: `bit_state`,
- new circuit classes: `Measurement`, `Layer`, `Circuit` (removing `CliffordLayer`, `MeasurementLayer`, `CliffordCircuit`)
- new gate and measurements: `SWAP`, `CZ`, `CX`, `measurement_layer`
- Remove debug `print` statements
### `utils.py`
- Unify projection trace and post-selection for stabilizer state.
- Introduce `stabilizer_postselect` to post-select a stabilizer state based on a projection operator (given as another stabilizer state), and returns the base-2 logarithm `log2prob` of the likelihood for the post-selection to succeed.
- All measurement and post-selection likelihood are given as log2 likelihood to avoid the underflow of the exponentially small probability for large system.
- Remove `stabilizer_projection_trace` that computes the trace of a projection operator with a stabilizer state density matrix. Functionality achieved by `stabilizer_postselect`, from which the trace can be computed as `2**log2prob`.
- Remove `stabilizer_postselection`, functionality replaced by `stabilizer_postselect`.
- Remove `decompose`, which decomposes a Pauli operator to product of stabilizers and destabilizers with phase factor. This functionality has been achieved by `obj.transform_by(state.to_map().inverse())` where `state` is the `StabilizerState` that hosts the stabilizers and destabilizers, and `obj` can be any Pauli operator (or operator list) to be decomposed. The idea is to use the state-map correspondence to construct the state encoder, then use the inverse map to construct the state decoder, and use the decoder to transform any operator back to the logical/syndrome basis.
- Add comments to explain the unified approach to realize `stabilizer_measure`, `stabilizer_postselect` and `stabilizer_project`.
### `paulialg.py`
- Remove `import qutip as qt` , and all `to_qutip` functions in all classes. Replace them by `to_numpy` functions, that returns matrix representation of Pauli algebra objects as numpy array, such that installation of `qutip` package will no longer be required.
- The `to_numpy` method for PauliList is now vectorized for efficiency, avoiding explicit Python loops.
- `PauliList` and `Pauli` constructors now accept `**kwargs` to absorb extra arguments from subclasses.
### `stabilizer.py`
- Bug fixed: The `StabilizerState.__init__(gs, r=0, **kwargs)` signature missed the `ps` argument. This results in the `.copy` method fails to make faithful copy for the stabilizer state (the phase factor will always be ignored under copy). The new signature `StabilizerState.__init__(*args, **kwargs)` handles the `.r` attribute initialization by `self.r=kwargs.pop(r,0)`.
- `StabilizerState` now only accepts r as a keyword argument, not as a positional argument.
- The `set_r` method has been removed; as `r` is set directly via the constructor or attribute assignment.
- All code that previously used `.set_r(r)` now uses `r=r` in the constructor or direct assignment.
- Default value for `r` is set to be 0 (indicating pure state) in all relevant classes and functions, instead of `None` in functions like `random_pauli_state` or `random_clifford_state`.
- Error handling: Custom `Error` exceptions have been replaced with standard exceptions like `ValueError`.
- The logic for stabilizer state expectation value and post-selection is more concise and robust, by using the `stabilizer_postselect` function uniformly for both trace evaluation and state projection.
- The `get_prob` method is now achieved by post-selecting the target bit-string state `bit_state` (computational basis state) and return the successful probability of the post-selection.
- Update the `.expect` method:
- Stabilizer state expectation value (trace with a projection operator) is now realized by `state_postselect`.
- Implement the fugacity setting to help re-weighting the Pauli observables expectation value for classical shadow purpos.
### `circuit.py`
- Restructure all circuit classes (`CliffordGate`, `Measurement`, `Layer`, `Circuit`) to enable measurements.
- All classes now has the `.unitary` property to indicate if the object is purely unitary or has measurement.
- The `.take()` method has been replaced by `.append()` for adding operations in all container classes (`Layer`, `Circuit`). This is to be consistent with the standard notation in other popular packages like Qiskit or Cirq.
- All `forward` and `backward` methods now always return the tuple (`obj`, `log2prob`), even for unitary processes (where `log2prob=0.0`). This unifies the interface and removes the need for branching on unitarity.
- New class `Measurement` is introduce at the same level of `CliffordGate` to perform computational basis measurement on designated qubits, such that mid-circuit measurement can be implemented.
- `CliffordLayer` and `MeasurementLayer` are unified into `Layer`, which can be either unitary or non-unitary, providing the ability to handle different type of operations together in a unified manner.
- The functionality of `MeasurementLayer` is replaced by a new function `measurement_layer(N)`, which returns a `Circuit` object that contains a single layer of measurement of all the N qubits in computational basis simultaneously, and can be used by appending to other circuit object.
- `Circuit` no longer takes qubit number `N` as input parameter. The `.N` attribute is also removed from the class, allowing circuit (as a collection of operations) to be defined without specifying the system size first.
- Instead, `Circuit` class constructor now takes circuit objects (`CliffordGate`, `Measurement`, `Layer`, `Circuit`) directly to build the circuit. Objects passing to the constructor will be appended to the circuit in chronological order.
- All references to `CliffordLayer`, `CliffordCircuit` are removed in favor of `Layer`, `Circuit`.
- Measurement outcome caching
- Measurement outcome will not be returned as output, but cached by the `Measurement` object during the forward pass, which is then reused for post-selection in the backward pass. This enables classical shadows to be collected by a forward and backward pass through the entire circuit systematically.
- Measurement outcomes are now stored only at the `Measurement` object level, with `Layer` and `Circuit` aggregating via properties.
- The measurement outcome property `out` is now a dynamic property at all levels, with setter and deleter for hierarchical management.
- Introduce `.reset()` method to reset the cached measurement outcome in `Measurement` object.
- Random unitary caching
- Random Clifford gate will sample its unitary map in the forward pass and cache it in `CliffordGate` object to be used by the backward pass (implementing the inverse unitary map). This enables classical shadows to be collected by a forward and backward pass through the entire circuit systematically.
- Introduce `.reset()` method for all classes to reset the cached Clifford maps in Clifford gates.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add generalized stabilizer development, expand methods, new utilities, and compatibility fixes
Add
dev_generalized_stabilizer.ipynbto the dev folderPauliChannelclass, representing a quantum channel in the Pauli basis.Usage: Encapsulates a set of Pauli operators and a channel matrix
phi, allowing channel action on density matrices viaGeneralizedStabilizerStateclass, representing mixed states as superpositions of destabilizer excitations over a stabilizer frame.Usage: Supports evolution by Pauli channels, operator representation, expectation value computation, and conversion to a full density matrix.
Add
expandmethod toPauli,PauliList,CliffordMap, andStabilizerStateUpdate for numpy 2.2 compatibility
numpy.complex_withnumpy.complex128throughout the codebase.Update docstrings and comments
^\daggerwith^Hin comments and docstrings to avoid issues with the special character\d.Enhance
pyclifford.utilswith new utility functionspauli_decompose(gs_in, ps_in, gs_stb, ps_stb, r):Usage: Decomposes Pauli operators into stabilizer and destabilizer components, returning binary encodings and phase indicators for the decomposition.
calculate_chi(chi_old, phi, fusion_map, fusion_p, L_new):Usage: Computes the updated density matrix in the excitation basis after fusion with a Pauli channel, used in generalized stabilizer state evolution.