ppvm_tableau::data
data¶
ppvm_tableau::data
Core [`Tableau`](data::Tableau) and [`GeneralizedTableau`](data::GeneralizedTableau) types.
classGeneralizedTableau¶source
ppvm_tableau::data::GeneralizedTableau
Bases: Clone, Display, Clifford, CliffordExtensions, CliffordBatch, CliffordExtensionsBatch, Reset, RotationOne<T>, RotXY<T>, RotationTwo<T>, TGate<T>, U3Gate<T>, LossyMeasure, LossyMeasureAll, TableauLike, Depolarizing<T>, PauliError<T>, TwoQubitPauliError<T>, Depolarizing2<T>, LossChannel<T>, AsymmetricLossChannel<T>, CorrelatedLossChannel<T>, ResetLossChannel<T>
Rust struct. A [`Tableau`] extended with sparse coefficient tracking to handle non-Clifford gates.
Signature
pub struct GeneralizedTableau<T: Config, IndexType, SparseVectorType: SparseVector<Complex<<T as >::Coeff>, IndexType>>Fields
| Name | Type | Description |
|---|---|---|
tableau | Tableau<T> | Underlying Clifford tableau. |
coefficients | SparseVectorType | Sparse coefficient vector indexed by bitstrings. |
is_lost | Vec<bool> | Per-qubit loss flags. |
coefficient_threshold | <T as >::Coeff | Coefficient-magnitude threshold below which branches are dropped. |
measurement_record | Vec<Option<bool>> | Ordered log of every measurement performed (mirrors stim's record). |
Non-Clifford gates (T, rotations) split a single tableau into a sum of weighted branches indexed by bitstrings. `GeneralizedTableau` stores those weights in a SparseVector keyed by an IndexType. Choose:
- `IndexType = usize` for up to 64 qubits,
- `IndexType = u128` for up to 128,
- `IndexType = bnum::types::U256` and friends for the very wide regime.
Per-qubit loss is tracked in is_lost; gates respect it automatically.
Examples
Section titled “Examples”Prepare a Bell pair and sample one shot. With a fixed seed the two measurements are perfectly correlated on every shot:
``` use ppvm_pauli_sum::config::fxhash::ByteF64; use ppvm_traits::traits::{Clifford, LossyMeasure}; use ppvm_tableau::data::GeneralizedTableau;
let mut tab: GeneralizedTableau<ByteF64<1>> = GeneralizedTableau::new_with_seed(2, 1e-12, 0); tab.h(0); tab.cnot(0, 1);
let r0 = LossyMeasure::measure(&mut tab, 0); let r1 = LossyMeasure::measure(&mut tab, 1); assert_eq!(r0, r1); ```
Non-Clifford gates work through the same interface — apply a `T` gate followed by `T†` and the state is unchanged:
``` use ppvm_pauli_sum::config::fxhash::ByteF64; use ppvm_traits::traits::{Clifford, TGate}; use ppvm_tableau::data::GeneralizedTableau;
let mut tab: GeneralizedTableau<ByteF64<1>> = GeneralizedTableau::new_with_seed(1, 1e-12, 0); tab.h(0); tab.t(0); tab.t_dag(0); // T followed by T† is the identity; the |+⟩ state is restored. ```
sourcemethodappend_measurement_record¶source
ppvm_tableau::data::GeneralizedTableau::append_measurement_record
pub fn append_measurement_record(&mut self, result: Option<bool>)Parameters
| Name | Type | Description |
|---|---|---|
result | Option<bool> |
Append an externally defined measurement result to the record.
Used by Stim instructions such as `MPAD`, which append measurement record bits without measuring a qubit.
sourcemethodbernoulli¶source
ppvm_tableau::data::GeneralizedTableau::bernoulli
pub fn bernoulli(&mut self, p: f64) -> boolParameters
| Name | Type | Description |
|---|---|---|
p | f64 |
Returns
bool
Sample a Bernoulli(`p`) outcome using the tableau’s internal RNG. Used by Stim measurement-noise dispatch in `ppvm-stim`.
sourcemethodcompute_decomposition¶source
ppvm_tableau::data::GeneralizedTableau::compute_decomposition
Signature
pub fn compute_decomposition(&self, addr0: usize, pauli: Pauli) -> (u8, I, I)Parameters
| Name | Type | Description |
|---|---|---|
addr0 | usize | |
pauli | Pauli |
Returns
(u8, I, I)
Compute the decomposition of a pauli into stabilizer destabilizer products Any Pauli can be written as P_addr0 = phase * prod(d_k ^ gamma_k) * prod(s_l ^ lambda_l) where: gamma_k == 1 iff {P_addr0, s_k} = 0 lambda_l == 1 iff {P_addr0, d_l} = 0 Lemma 5. from T. J. Yoder (2012) NOTE: this is O(n^2)
The function returns `(phase, stab_anticomm_bits, destab_anticomm_bits)`, where `stab_anticomm_bits[k] = 1` iff P_addr0 anticommutes with stabilizer s_k, and `destab_anticomm_bits[l] = 1` iff P_addr0 anticommutes with destabilizer d_l. Note that stab_anticomm_bits is equal to the shift of the index when branching (`beta` in Eq(4) of the SOFT paper).
sourcemethodcompute_overlap_case_a¶source
ppvm_tableau::data::GeneralizedTableau::compute_overlap_case_a
Signature
pub fn compute_overlap_case_a(coeff_map: &HashMap<I, Complex<<T as >::Coeff>>, phase_decomp: u8, destab_anticomm_bits: I, stab_anticomm_bits: I, odd_phase_mask: I) -> f64Parameters
| Name | Type | Description |
|---|---|---|
coeff_map | &HashMap<I, Complex<<T as >::Coeff>> | |
phase_decomp | u8 | |
destab_anticomm_bits | I | |
stab_anticomm_bits | I | |
odd_phase_mask | I |
Returns
f64
Case_a overlap: cross-index pairing via HashMap lookup. Accumulates only the real part of z_overlap.
sourcemethodcompute_overlap_case_b¶source
ppvm_tableau::data::GeneralizedTableau::compute_overlap_case_b
Signature
pub fn compute_overlap_case_b(entries: &[(Complex<<T as >::Coeff>, I)], phase_decomp: u8, destab_anticomm_bits: I) -> f64Parameters
| Name | Type | Description |
|---|---|---|
entries | &[(Complex<<T as >::Coeff>, I)] | |
phase_decomp | u8 | |
destab_anticomm_bits | I |
Returns
f64
Case_b overlap: self-pairing (branch_index = idx), so overlap = ±|c|^2. Only even phases contribute to the real part.
sourcemethodcurrent_measurement_record¶source
ppvm_tableau::data::GeneralizedTableau::current_measurement_record
pub fn current_measurement_record(&self) -> &[Option<bool>]Returns
&[Option<bool>]
All measurement outcomes recorded so far, in order.
sourcemethodcz_block¶source
ppvm_tableau::data::GeneralizedTableau::cz_block
Signature
pub fn cz_block(&mut self, control_base: usize, target_base: usize, count: usize)Parameters
| Name | Type | Description |
|---|---|---|
control_base | usize | |
target_base | usize | |
count | usize |
Apply CZ to `count` pairs with a constant offset, given in qubit-index terms: `(control_base + i, target_base + i)` for `i in 0..count`.
This is the high-level entry point for a fused block of CZs: it splits the run at storage-word boundaries internally and dispatches each segment to Self::cz_block_pairs (control and target in the same word) or Self::cz_block_pairs_cross_word (straddling two words), so callers never need to reason about the `u64` packing. CZ is symmetric, so the two bases may be passed in either order.
Overlapping pairs — `|target_base - control_base| < count`, of which adjacent-pair brickwork `cz_block(0, 1, n)` is the extreme case — are handled: the same-word segments route through Self::cz_block_pairs into Tableau::cz_block_pairs, which falls back to the per-pair loop. A cross-word segment is always disjoint (its `run` never exceeds the offset), so the fused kernel stays live there.
sourcemethodcz_block_pairs¶source
ppvm_tableau::data::GeneralizedTableau::cz_block_pairs
Signature
pub fn cz_block_pairs(&mut self, base: usize, offset: usize, count: usize)Parameters
| Name | Type | Description |
|---|---|---|
base | usize | |
offset | usize | |
count | usize |
Apply CZ to N pairs with constant offset: (base+i, base+offset+i) for i in 0..count. Falls back to individual CZ calls if any qubit in the range is lost. Overlap (`offset < count`) is owned by Tableau::cz_block_pairs.
sourcemethodcz_block_pairs_cross_word¶source
ppvm_tableau::data::GeneralizedTableau::cz_block_pairs_cross_word
Signature
pub fn cz_block_pairs_cross_word(&mut self, word_c: usize, base_bit_c: usize, word_t: usize, base_bit_t: usize, count: usize)Parameters
| Name | Type | Description |
|---|---|---|
word_c | usize | |
base_bit_c | usize | |
word_t | usize | |
base_bit_t | usize | |
count | usize |
Apply CZ to N cross-word pairs. Controls at word_c, targets at word_t. Falls back to individual CZ calls if any qubit is lost.
sourcemethodexpectation¶source
ppvm_tableau::data::GeneralizedTableau::expectation
pub fn expectation<W: PauliWordTrait>(&self, word: &W) -> f64Parameters
| Name | Type | Description |
|---|---|---|
word | &W |
Returns
f64
`⟨ψ|word|ψ⟩` for the multi-qubit Pauli `word`.
Conjugates `word` through the Clifford tableau (giving a Pauli on the canonical basis: an X-mask, Z-mask, and `i^φ` phase), then sums `⟨α|P_conj|β⟩ c_α* c_β` over the sparse coefficient vector. Always returns a real number (Hermitian operator on a normalized state).
sourcemethodflip_with_prob¶source
ppvm_tableau::data::GeneralizedTableau::flip_with_prob
pub fn flip_with_prob(&mut self, bit: bool, p: f64) -> boolParameters
| Name | Type | Description |
|---|---|---|
bit | bool | |
p | f64 |
Returns
bool
Flip `bit` with probability `p`. Used by Stim MR/MPad readout-noise dispatch in `ppvm-stim`. Returns `bit` unchanged when `p <= 0.0`.
sourcemethodfork¶source
ppvm_tableau::data::GeneralizedTableau::fork
pub fn fork(&self, seed: Option<u64>) -> SelfParameters
| Name | Type | Description |
|---|---|---|
seed | Option<u64> |
Returns
Self
Clone the quantum state but reinitialize the RNG, producing an independent simulation branch. If `seed` is `Some`, the new RNG is seeded deterministically; if `None`, it is seeded from OS entropy.
sourcemethodmeasure_all_with_scratch¶source
ppvm_tableau::data::GeneralizedTableau::measure_all_with_scratch
Signature
pub fn measure_all_with_scratch(&mut self, scratch: &mut MeasureScratch<I, <T as >::Coeff>) -> Vec<Option<bool>>Parameters
| Name | Type | Description |
|---|---|---|
scratch | &mut MeasureScratch<I, <T as >::Coeff> |
Returns
Vec<Option<bool>>
Same as LossyMeasureAll::measure_all, but the caller supplies a `MeasureScratch` that’s reused across the n per-qubit measurements (and, if the caller chooses, across many invocations / shots).
This is the entry point samplers should use when running many shots: initialize one scratch alongside the sampler and thread it through every shot to amortize the case-a HashMap and b_entries allocations.
sourcemethodmeasure_many_with_scratch¶source
ppvm_tableau::data::GeneralizedTableau::measure_many_with_scratch
Signature
pub fn measure_many_with_scratch(&mut self, indices: &[usize], scratch: &mut MeasureScratch<I, <T as >::Coeff>) -> Vec<Option<bool>>Parameters
| Name | Type | Description |
|---|---|---|
indices | &[usize] | |
scratch | &mut MeasureScratch<I, <T as >::Coeff> |
Returns
Vec<Option<bool>>
Measure the given qubit `indices` in order, reusing a caller-supplied `MeasureScratch` across the per-index measurements (and, if the caller chooses, across many invocations / shots) — the explicit-index analogue of measure_all_with_scratch. This is the scratch-reusing engine behind the tableau’s `LossyMeasure::measure_many` override (in `measure.rs`), which the Stim executor and the Python `measure_many` binding both go through.
sourcemethodmeasure_noisy¶source
ppvm_tableau::data::GeneralizedTableau::measure_noisy
Signature
pub fn measure_noisy(&mut self, addr0: usize, flip_prob: f64) -> Option<bool>Parameters
| Name | Type | Description |
|---|---|---|
addr0 | usize | |
flip_prob | f64 |
Returns
Option<bool>
Measure qubit `addr0` in Z basis with optional readout noise.
Behaves like measure, then with probability `flip_prob` flips the recorded bit. The qubit’s quantum state stays consistent with the true outcome — only the returned value flips. `flip_prob = 0.0` is equivalent to `measure`.
If the qubit is lost, returns `None` regardless of `flip_prob`.
sourcemethodn_qubits¶source
ppvm_tableau::data::GeneralizedTableau::n_qubits
methodnew¶source
ppvm_tableau::data::GeneralizedTableau::new
Signature
pub fn new(n_qubits: usize, coefficient_threshold: <T as >::Coeff) -> SelfParameters
| Name | Type | Description |
|---|---|---|
n_qubits | usize | |
coefficient_threshold | <T as >::Coeff |
Returns
Self
Construct a generalized tableau in the `|0…0⟩` state.
Branches whose coefficient magnitude falls below `coefficient_threshold` are dropped during gate application.
sourcemethodnew_with_seed¶source
ppvm_tableau::data::GeneralizedTableau::new_with_seed
Signature
pub fn new_with_seed(n_qubits: usize, coefficient_threshold: <T as >::Coeff, seed: u64) -> SelfParameters
| Name | Type | Description |
|---|---|---|
n_qubits | usize | |
coefficient_threshold | <T as >::Coeff | |
seed | u64 |
Returns
Self
Same as GeneralizedTableau::new, but seed the RNG deterministically.
sourcemethododd_phase_destabilizer_mask¶source
ppvm_tableau::data::GeneralizedTableau::odd_phase_destabilizer_mask
pub fn odd_phase_destabilizer_mask(&self) -> IReturns
I
Build a bitmask where bit i is set if destabilizer i has odd phase (phase % 2 != 0).
sourcemethodoverwrite_last_measurement_record¶source
ppvm_tableau::data::GeneralizedTableau::overwrite_last_measurement_record
Signature
pub fn overwrite_last_measurement_record(&mut self, result: Option<bool>)Parameters
| Name | Type | Description |
|---|---|---|
result | Option<bool> |
Replace the most recent measurement record entry.
Used by noisy measurement paths where the quantum state follows the true outcome but the public record should hold the reported bit.
sourcemethodproject_case_a¶source
ppvm_tableau::data::GeneralizedTableau::project_case_a
Signature
pub fn project_case_a(&mut self, outcome: bool, scratch: &mut MeasureScratch<I, <T as >::Coeff>, phase_decomp: u8, stab_anticomm_bits: I, destab_anticomm_bits: I, addr0: usize)Parameters
| Name | Type | Description |
|---|---|---|
outcome | bool | |
scratch | &mut MeasureScratch<I, <T as >::Coeff> | |
phase_decomp | u8 | |
stab_anticomm_bits | I | |
destab_anticomm_bits | I | |
addr0 | usize |
methodproject_case_b¶source
ppvm_tableau::data::GeneralizedTableau::project_case_b
Signature
pub fn project_case_b(&mut self, entries: &[(Complex<<T as >::Coeff>, I)], outcome: bool, phase_decomp: u8, destab_anticomm_bits: I)Parameters
| Name | Type | Description |
|---|---|---|
entries | &[(Complex<<T as >::Coeff>, I)] | |
outcome | bool | |
phase_decomp | u8 | |
destab_anticomm_bits | I |
project state in case b (Z is a stabilizer) according to sampled outcome
sourcemethodreset_all¶source
ppvm_tableau::data::GeneralizedTableau::reset_all
pub fn reset_all(&mut self)methodtrace¶source
ppvm_tableau::data::GeneralizedTableau::trace
pub fn trace(&self, pattern: &PauliPattern) -> f64Parameters
| Name | Type | Description |
|---|---|---|
pattern | &PauliPattern |
Returns
f64
`Σ_{P matches pattern} ⟨ψ|P|ψ⟩`.
Enumerates every `PauliWord` accepted by `pattern` via PauliPattern::enumerate_matches and sums their expectations. Star quantifiers (`X*`) panic — the pattern must be bounded; use counted repetition (`Z?{n}`) or positional anchors instead.
sourcemethodz_expectation¶source
ppvm_tableau::data::GeneralizedTableau::z_expectation
pub fn z_expectation(&self, addr0: usize) -> f64Parameters
| Name | Type | Description |
|---|---|---|
addr0 | usize |
Returns
f64
`⟨Z⟩` on qubit `addr0`, computed non-destructively (the state is not collapsed). Reuses the measurement overlap machinery; cost scales with the number of coefficients (and n²).
sourceclassTableau¶source
ppvm_tableau::data::Tableau
Bases: Clone, Debug, Display, Clifford, CliffordExtensions, CliffordBatch, CliffordExtensionsBatch, Reset, Measure, TableauLike, Depolarizing<T>, PauliError<T>, TwoQubitPauliError<T>, Depolarizing2<T>
Rust struct. A `2n`-row stabilizer / destabilizer tableau.
pub struct Tableau<T: Config>Fields
| Name | Type | Description |
|---|---|---|
n_qubits | usize | Number of qubits. |
data | Vec<PhasedPauliWord<<T as >::Storage, <T as >::BuildHasher, PauliWord<<T as >::Storage, <T as >::BuildHasher, false>>> | Destabilizer / Stabilizer tableau * Entries 0..n are the destabilizers * Entries n..2n are the stabilizers |
Rows `0..n` hold the destabilizers; rows `n..2n` hold the stabilizers. Each row is a PhasedPauliWord tracking both its `X`/`Z` bits and a phase in `{±1, ±i}`. Implements every Clifford-only operation natively (Hadamard, phase, CNOT, CZ, etc.).
Examples
Section titled “Examples”``` use ppvm_pauli_sum::config::fxhash::ByteF64; use ppvm_traits::traits::Clifford; use ppvm_tableau::data::Tableau;
let mut tab: Tableau<ByteF64<1>> = Tableau::new(2); tab.h(0); tab.cnot(0, 1); assert_eq!(tab.n_qubits, 2); assert_eq!(tab.stabilizers().len(), 2); ```
sourcemethodcz_block_pairs¶source
ppvm_tableau::data::Tableau::cz_block_pairs
Signature
pub fn cz_block_pairs(&mut self, base: usize, offset: usize, count: usize)Parameters
| Name | Type | Description |
|---|---|---|
base | usize | |
offset | usize | |
count | usize |
Apply CZ to N pairs with constant offset: (base+i, base+offset+i) for i in 0..count. All pairs must be in the same u64 word. This replaces N individual CZ calls with a single word-level shift+XOR operation per row.
Preconditions
Section titled “Preconditions”The fused kernel needs `offset >= count`, i.e. pairwise-disjoint supports (pairs `i != j` collide iff `|i - j| == offset`, which needs `offset < count`, or `offset == 0`). Both fused updates need it: the single `count_ones() & 1` phase reads the pre-update `z` plane for every pair at once (`Batch.lean::czSeq_phase` proves it equal to the sequential loop only under pairwise disjointness, and `czSeq_phase_needs_disjoint` exhibits a counterexample), and the `z` delta `OR`s the two shifted planes, so a `z` bit written by two pairs is set once instead of XOR-cancelled.
Overlapping pairs (`offset < count`, including `offset == 0`) fall back to the per-pair `cz` loop, so this method is safe in release.
Panics
Section titled “Panics”Debug-asserts that all bits are within the same word.
sourcemethodcz_block_pairs_cross_word¶source
ppvm_tableau::data::Tableau::cz_block_pairs_cross_word
Signature
pub fn cz_block_pairs_cross_word(&mut self, word_c: usize, base_bit_c: usize, word_t: usize, base_bit_t: usize, count: usize)Parameters
| Name | Type | Description |
|---|---|---|
word_c | usize | |
base_bit_c | usize | |
word_t | usize | |
base_bit_t | usize | |
count | usize |
Apply CZ to N pairs with constant offset across two different words. Controls at (word_c, base_bit_c+i) and targets at (word_t, base_bit_t+i) for i in 0..count. word_c and word_t must be different.
sourcemethoddestabilizers¶source
ppvm_tableau::data::Tableau::destabilizers
Signature
pub fn destabilizers(&self) -> &[PhasedPauliWord<<T as >::Storage, <T as >::BuildHasher, PauliWord<<T as >::Storage, <T as >::BuildHasher, false>>]Returns
&[PhasedPauliWord<<T as >::Storage, <T as >::BuildHasher, PauliWord<<T as >::Storage, <T as >::BuildHasher, false>>]
View of the destabilizer rows (the lower half of the tableau).
sourcemethoddestabilizers_mut¶source
ppvm_tableau::data::Tableau::destabilizers_mut
Signature
pub fn destabilizers_mut(&mut self) -> &mut [PhasedPauliWord<<T as >::Storage, <T as >::BuildHasher, PauliWord<<T as >::Storage, <T as >::BuildHasher, false>>]Returns
&mut [PhasedPauliWord<<T as >::Storage, <T as >::BuildHasher, PauliWord<<T as >::Storage, <T as >::BuildHasher, false>>]
Mutable view of the destabilizer rows.
sourcemethodnew¶source
ppvm_tableau::data::Tableau::new
pub fn new(n_qubits: usize) -> SelfParameters
| Name | Type | Description |
|---|---|---|
n_qubits | usize |
Returns
Self
Construct a fresh tableau initialised to `|0…0⟩`.
sourcemethodnew_with_seed¶source
ppvm_tableau::data::Tableau::new_with_seed
pub fn new_with_seed(n_qubits: usize, seed: u64) -> SelfParameters
| Name | Type | Description |
|---|---|---|
n_qubits | usize | |
seed | u64 |
Returns
Self
Same as Tableau::new, but seed the RNG deterministically.
sourcemethodreset_all¶source
ppvm_tableau::data::Tableau::reset_all
pub fn reset_all(&mut self)methodstabilizers¶source
ppvm_tableau::data::Tableau::stabilizers
Signature
pub fn stabilizers(&self) -> &[PhasedPauliWord<<T as >::Storage, <T as >::BuildHasher, PauliWord<<T as >::Storage, <T as >::BuildHasher, false>>]Returns
&[PhasedPauliWord<<T as >::Storage, <T as >::BuildHasher, PauliWord<<T as >::Storage, <T as >::BuildHasher, false>>]
View of the stabilizer rows (the upper half of the tableau).
sourcemethodstabilizers_mut¶source
ppvm_tableau::data::Tableau::stabilizers_mut
Signature
pub fn stabilizers_mut(&mut self) -> &mut [PhasedPauliWord<<T as >::Storage, <T as >::BuildHasher, PauliWord<<T as >::Storage, <T as >::BuildHasher, false>>]Returns
&mut [PhasedPauliWord<<T as >::Storage, <T as >::BuildHasher, PauliWord<<T as >::Storage, <T as >::BuildHasher, false>>]
Mutable view of the stabilizer rows.
sourcefunctionsymplectic_inner¶source
ppvm_tableau::data::symplectic_inner
pub fn symplectic_inner<I>(alpha: I, beta: I) -> u32Parameters
| Name | Type | Description |
|---|---|---|
alpha | I | |
beta | I |
Returns
u32
Symplectic inner product of two tableau index values — the count of shared set bits, used in stabilizer phase calculations.
source