Built directly from in-source docstrings: rustdoc JSON for the Rust crates, griffe for the Python package. Use the language filter to narrow the view; the search box always queries across both.
Python items 121Rust items 123Generated
Pythonppvm
Pythonic wrapper around the native Rust runtime. All items below live under the ppvm top-level module.
Generalized stabilizer tableau for quantum circuit simulation.
Full docstring
Generalized stabilizer tableau for quantum circuit simulation.
Represents an arbitrary quantum state in the basis spanned by the
stabilizer tableau. It supports Clifford gates, arbitrary single- and two-qubit rotations,
noise channels, and mid-circuit measurement.
The coefficient vector grows exponentially with the
number of non-Clifford operations applied.
Multi-shot sampling — module-level alias for GeneralizedTableau.sample.
Full docstring
Multi-shot sampling — module-level alias for GeneralizedTableau.sample.
When n_qubits is None (the default) the qubit count is inferred from
the program; see GeneralizedTableau.sample. Shots are sampled in parallel
across CPU cores with the GIL released; see GeneralizedTableau.sample for
seeding and RAYON_NUM_THREADS.
Apply a state-dependent ("asymmetric") loss channel to a qubit.
Full docstring
Apply a state-dependent ("asymmetric") loss channel to a qubit.
The qubit is lost from |0> with probability p0 and from |1> with
probability p1, so the total loss probability depends on the
current populations: p_tot = p0 * (1 + <Z>)/2 + p1 * (1 - <Z>)/2.
.. note::
This is the trajectory approximation of the loss channel. It is
exact for the loss statistics and in the symmetric limit
p0 == p1 (where it matches loss_channel), but it does
not apply the survival back-action, so for p0 != p1 the
surviving qubit's state is left unchanged. See issue #39.
Return a snapshot of the sparse coefficient vector.
Full docstring
Return a snapshot of the sparse coefficient vector.
The tableau represents the state as a sparse superposition over the
basis states of its stabilizer frame. This returns that vector as a
mapping from basis-state index to complex amplitude.
The returned dict is a copy: mutating it does not affect the tableau's
internal state.
Returns
A dict mapping each populated basis-state index to its complex amplitude.
Apply a fused block of CZ gates over constant-offset qubit pairs.
Full docstring
Apply a fused block of CZ gates over constant-offset qubit pairs.
Applies CZ to (control_base + i, target_base + i) for i in
range(count) -- i.e. the gates zip(range(control_base, ...), range(target_base, ...)) would produce. This uses a word-level kernel
that is much faster than the equivalent cz call when the pairs form a
contiguous, constant-offset block (e.g. entangling two adjacent qubit
registers). For scattered pairs, use cz.
CZ is symmetric, so control_base and target_base may be given in
either order.
Compute ⟨ψ|word|ψ⟩ for a single multi-qubit Pauli string.
Full docstring
Compute ⟨ψ|word|ψ⟩ for a single multi-qubit Pauli string.
Parameters
wordstr
A dense Pauli string with one character per qubit, each
drawn from I, X, Y, Z (e.g. "ZZ" for a
two-qubit state). Its length must equal the tableau's qubit
count.
Returns
float
The real expectation value of word in the current state.
Fork this tableau into an independent simulation branch.
Full docstring
Fork this tableau into an independent simulation branch.
Clones all quantum state but reinitializes the RNG, so the returned
tableau evolves independently from this one. If seed is provided
the new RNG is seeded deterministically; otherwise it is seeded from
OS entropy.
Use this when branching a simulation into independent trajectories.
To preserve the RNG state exactly (e.g. for checkpointing), use
copy.copy() or copy.deepcopy() instead.
Parameters
seed
Optional integer seed for the forked RNG.
Returns
GeneralizedTableau
A new GeneralizedTableau with the same quantum state but an independent RNG.
Execute a parsed Stim program against this tableau (single shot).
Full docstring
Execute a parsed Stim program against this tableau (single shot).
.. note::
This mutates the tableau in place. For independent shots use
fork or ppvm.sample_stim / sample helpers (which build a
fresh tableau per shot).
Run num_shots shots of prog and return all measurement results.
Full docstring
Run num_shots shots of prog and return all measurement results.
Each shot starts from a fresh tableau, so this is the right entry
point for multi-shot sampling.
When n_qubits is None (the default) the qubit count is inferred
from the program via prog.num_qubits (one past the highest qubit
index it references), falling back to 1 for a program that touches no
qubits. Pass an explicit n_qubits to size the tableau larger.
Shots run in parallel across CPU cores (the GIL is released during
sampling), with a serial fallback for small batches. When seed is
given (it must fit in an unsigned 64-bit integer), shot i uses
(seed + i) % 2**64 (wrapping u64 arithmetic), so results are
reproducible and independent of the number of threads. Set the
RAYON_NUM_THREADS environment variable before the first call to
control the pool size (it defaults to the number of logical cores).
Sum Pauli expectations over every word matching pattern.
Full docstring
Sum Pauli expectations over every word matching pattern.
Enumerates each PauliWord accepted by pattern and returns
the sum of their expectations. Star quantifiers (X*) are not
supported — use counted repetition (Z?{n}) or positional
anchors instead.
A sum over generalized tableaus, representing a density matrix that is a weighted sum over pure state projectors (classical mixture). Each projector is represented by a generalized tableau. The respective weights are the probabilities wi…
Full docstring
A sum over generalized tableaus, representing a density matrix that is a
weighted sum over pure state projectors (classical mixture). Each projector
is represented by a generalized tableau. The respective weights are the
probabilities with which the system is in the corresponding state.
p[0]: probability of losing both qubits simultaneously
when both are in the qubit subspace.
p[1]: probability of losing a named one of the two
qubits when both are in the qubit subspace. The two
single-loss events are disjoint, so the probability of losing
exactly one qubit is 2 * p[1] and the probability that
both remain is 1 - 2 * p[1] - p[0] (which qubit is lost is
50/50 random).
p[2]: probability of losing the remaining active qubit
when the other has already been lost prior to this channel.
Convert a dictionary of two-qubit Pauli error probabilities to a list.
Full docstring
Convert a dictionary of two-qubit Pauli error probabilities to a list.
Convenience method to convert a dictionary mapping two-qubit Pauli
strings (e.g., "IX", "ZZ") to their probabilities into the ordered
list format required by two_qubit_pauli_error.
Parameters
error_probabilities
Dictionary mapping two-qubit Pauli strings
to their error probabilities. Missing keys default to 0.0.
Returns
A list of 15 probabilities in the canonical order (excludes "II").
If True (default), run the configured truncation
strategy after the gate; if False, leave the map
untruncated so the next gate sees the full unpruned state.
Apply RXX (Ising XX) rotation gates over consecutive qubit pairs.
Full docstring
Apply RXX (Ising XX) rotation gates over consecutive qubit pairs.
See RotationsMixin.rxx for the gate definition.
Parameters
*targets
A flat list of qubit indices, broadcast as pairs.
theta
The rotation angle in radians.
truncatebool
If True (default), run the configured truncation
strategy after the gate. Set to False to compose a
U(1)-conserving step like rxx + ryy on the same edge
without dropping the conserved-charge component between
them — then call PauliSum.truncate once at the
end of the composition.
This is achieved by extending the set of Pauli basis operators to include
an addition operator {I, X, Y, Z, L}, where L is the projector on
a third leakage state. This basis effectively allows simulating qutrits,
where we neglect any coherences between the qubit subspace and the leakage
state.
In addition to the new channels, there is also another truncation strategy:
Since Pauli Strings that have an L at multiple positions contribute only
minimally, these can get truncated by setting an appropriate max_loss_weight.
The truncation is similar to how max_pauli_weight truncates strings, but only
counting Ls.
A weighted sum of Pauli strings for quantum simulation.
Full docstring
A weighted sum of Pauli strings for quantum simulation.
PauliSum represents a linear combination of Pauli operators, commonly used
to represent quantum observables or Hamiltonians. It provides methods for
applying quantum gates (Clifford operations and rotations) and computing
expectation values via the trace operation.
This applies a correlated loss channel to the qubits at addr0 and addr1.
The channel accepts 3 probabilities as argument:
* p[0]: The probability of losing both qubits, when they are originally
in the qubit subspace.
* p[1]: The probability of losing a named one of the two qubits,
when both qubits are originally in the qubit subspace. The two
single-loss events are disjoint, so the probability of losing
exactly one qubit is 2 * p[1], and the probability that
both qubits remain in the qubit subspace — the factor a fully
in-subspace observable is scaled by — is 1 - 2 * p[1] - p[0].
* p[2]: The probability of losing one qubit when the other one
has already been lost prior to applying the channel. This is to
account for the fact that when one qubit is missing during e.g.
a controlled gate, the remaining qubit undergoes a different dynamic.
We account for this difference with this distinct probability.
truncate: If True (default), run the configured truncation
strategy after the channel; if False, defer it.
Reduces the trace of qubit-subspace operators by (1 - p).
Adds back population into I or Z if the Pauli string has an L
at addr0. This can only occur if a reset channel has been applied before
and accounts for the fact of falsely counting a lost qubit as 0 in a
measurement.
Parameters
addr0int
The index of the target qubit.
pfloat
Loss probability in [0, 1].
truncatebool
If True (default), run the configured truncation
strategy after the channel; if False, defer it.
Reset a lost qubit to the 0 state. Usually, you want to apply this channel at the end of the circuit, i.e. at the beginning when propagating backwards.
Full docstring
Reset a lost qubit to the 0 state. Usually, you want to apply
this channel at the end of the circuit, i.e. at the beginning when
propagating backwards.
NOTE: This channel causes exponential branching in I and Z.
Make sure to set an appropriate max_loss_weight to truncate.
Parameters
addr0int
The index of the qubit to reset.
truncatebool
If True (default), run the configured truncation
strategy after the channel; if False, defer it.
Create a PauliSum from one or more terms with flexible input formats.
Full docstring
Create a PauliSum from one or more terms with flexible input formats.
Parameters
n_qubitsint
Number of qubits.
terms
A single term or list of terms. Each term is either:
A full Pauli string (e.g. "IX"), with coefficient 1.0.
A compact string "P{i}" (e.g. "X1"), placing Pauli P
at 0-based qubit index i with coefficient 1.0.
A tuple (str, float) pairing either of the above with an
explicit coefficient.
min_abs_coefffloat
Terms with absolute coefficient below this threshold
are dropped. Defaults to 1e-10.
max_pauli_weight
Maximum number of non-identity Paulis per term.
If None, truncation is disabled.
max_loss_weight
Maximum loss weight per term (only used by
LossyPauliSum). If None, truncation is disabled.
Note, that this should usually be chosen to be quite low, since
e.g. 10 would correspond to keeping terms that contribute if
up to 10 qubits are lost simultaneously.
preserve_strings
Pauli strings (length n_qubits each) that
truncation must never drop. Empty by default.
Returns
Self
A new instance of the class this method is called on.
Raises
ValueError
If a compact qubit index is out of range for n_qubits.
A pattern specifying which terms to include in the trace.
Use 'Z' to project onto |0>, '?' for any single character,
and '*' to match zero or more characters.
Run the configured truncation strategy (min_abs_coeff and/or max_pauli_weight) on the current state.
Full docstring
Run the configured truncation strategy (min_abs_coeff and/or
max_pauli_weight) on the current state.
Useful when gates were called with truncate=False to chain a
composition of commuting operations (e.g. rxx + ryy on the
same edge, an exchange-like step that conserves total Z), so
that intermediate truncation does not drop conserved-charge
components. Call truncate once at the end of the
composition to apply the cut to the combined result.
The four standard Paulis are encoded so that the low two bits identify
the operator (I = 0b00, X = 0b01, Z = 0b10, Y = 0b11), which is
the same encoding stabilizer-formalism tools commonly use. The extra
variant Pauli::L marks a qubit as lost, used by the loss-aware
portions of the runtime.
Config is a zero-cost generic dispatch mechanism: it pins down the
storage backing (Storage), coefficient type (Coeff), truncation
strategy (Strategy), hasher (BuildHasher), Pauli-word representation
(PauliWordType), and concrete map (Map) used by a single
PauliSum instantiation. Pre-built bundles live in the submodules
(fxhash, indexmap, …); user code may define its own.
Two-qubit Pauli rotations, generated by P_a ⊗ P_b for any pair of
non-identity Paulis. Provides the named convenience methods
rxx, rxy, …, rzz on top of the generic [rotate_2].
Full docstring
Two-qubit Pauli rotations, generated by P_a ⊗ P_b for any pair of
non-identity Paulis. Provides the named convenience methods
rxx, rxy, …, rzz on top of the generic rotate_2.
The minimal Clifford gate set: the single-qubit Paulis (X, Y, Z),
Hadamard (H), phase gate (S), and the two entangling Cliffords
CNOT and CZ.
Full docstring
The minimal Clifford gate set: the single-qubit Paulis (X, Y, Z),
Hadamard (H), phase gate (S), and the two entangling Cliffords
CNOT and CZ.
Implemented by PauliSum, by every tableau type, and — via the
blanket impl in this module — by every PauliWordTrait
implementor.
Examples
Build the GHZ-preparation circuit on a PauliSum (Heisenberg picture,
so gates are applied in reverse). PauliSum lives in the downstream
ppvm-pauli-sum crate, so this example is ignored here:
use ppvm_pauli_sum::prelude::*;
let mut state: PauliSum<config::indexmap::ByteFxHashF64<1>> =
PauliSum::builder().n_qubits(2).build();
state += ("ZZ", 1.0);
state.cnot(0, 1);
state.h(0);
assert_eq!(state.len(), 1);
Batched Clifford gates: apply the same gate to many qubits in one call.
Full docstring
Batched Clifford gates: apply the same gate to many qubits in one call.
Default implementations loop over the corresponding single-qubit
(or single-pair) method on Clifford. Types like the stabilizer
Tableau override these methods with a fused inner-loop or bitmask
implementation. Types that don't need specialization can implement
this trait with an empty impl to use the defaults.
Numeric coefficient type usable inside a
PauliSum.
Full docstring
Numeric coefficient type usable inside a
PauliSum.
Coefficient bundles every arithmetic operation a Pauli-propagation
step needs — addition, multiplication, signed multiplication, half,
sin/cos, and a cutoff predicate used by truncation strategies. The
built-in f64 impl covers the common case; Complex<f64> is
available when phase tracking is required.
Post-processing that a PauliWord's hasher
applies to its raw 64-bit digest before it is cached as the map-key hash.
Full docstring
Post-processing that a PauliWord's hasher
applies to its raw 64-bit digest before it is cached as the map-key hash.
The cached value is what hashbrown ultimately splits into a bucket index
(low bits) and a control-byte tag (top 7 bits). Whether the raw digest is
good enough for that split is a property of the hasher, not of the
Pauli word: an AES-based hasher such as gxhash avalanches well even for an
8-byte key, whereas FxHasher — a couple of multiply-rotate rounds — leaves
the low bits of a short key highly correlated and needs a fold to fix them.
Keeping this on the hasher is what makes the abstraction correct. An earlier
version folded based on storage width alone, which conflated "narrow
storage" with "weak hasher" and so wrongly folded gxhash too. Here each
hasher declares how (if at all) its output must be adjusted, and
PauliWord::rehash just defers to it. The default
is the identity — the right choice for any hasher that already distributes
its low bits well — so a custom hasher opts in with a bare
impl HashFinalize for MyHasher {}.
State-dependent ("asymmetric") single-qubit loss channel: a qubit is
lost from |0⟩ with probability p0 and from |1⟩ with probability
p1. Unlike LossChannel, the total loss probability depends on the
qubit's populations, so th…
Full docstring
State-dependent ("asymmetric") single-qubit loss channel: a qubit is
lost from |0⟩ with probability p0 and from |1⟩ with probability
p1. Unlike LossChannel, the total loss probability depends on the
qubit's populations, so the channel reads the current ⟨Z⟩.
This is the one place the parameterization is defined; every backend
(ppvm-pauli-sum, ppvm-tableau's trajectory, ppvm-tableau-sum's mixture)
and every binding cites it rather than restating it. In the paper's notation
p = [p_LL, p_LQ, p_LN], and p[1] = p_LQ is the probability that a
named one of the two atoms is lost while the other survives. The two
single-loss events are disjoint, so
the probability of losing exactly one atom is 2·p[1], and
the probability that both remain in the qubit subspace — the factor a
fully in-subspace observable is scaled by — is 1 − 2·p[1] − p[0].
The channel is completely positive exactly on p[0], p[1] >= 0,
p[0] + 2·p[1] <= 1, p[2] ∈ [0, 1]; the tableau backends debug_assert
that region.
Backing storage for a PauliWord — a
fixed-size, Copy-able block of bits (typically [u8; N] or
[u64; N]).
Full docstring
Backing storage for a PauliWord — a
fixed-size, Copy-able block of bits (typically [u8; N] or
[u64; N]).
The bytemuck::Pod bound guarantees the storage is a plain-old-data
type with no padding and all bit patterns valid, which lets
PauliWord::rehash view it as a &[u8] for
fast byte-slice hashing withoutunsafe. Every concrete storage used
here ([u8; N], [u64; N]) is already Pod.
A truncation policy applied to a PauliSum
during Pauli propagation.
Full docstring
A truncation policy applied to a PauliSum
during Pauli propagation.
The strategy controls two things: the initial capacity allocated
for the underlying map (estimating how many Pauli paths the
simulation is expected to generate) and the cut applied when
truncate is called explicitly. Implementations
include NoStrategy, the coefficient-magnitude threshold, the
max-Pauli-weight bound, etc.
Word-level Pauli operations. Types implementing this trait automatically
gain crate::traits::Clifford and crate::traits::CliffordExtensions
behavior via blanket impls in the crate::traits::clifford module, using
get_lbit / `g…
Full docstring
Word-level Pauli operations. Types implementing this trait automatically
gain crate::traits::Clifford and crate::traits::CliffordExtensions
behavior via blanket impls in the crate::traits::clifford module, using
get_lbit / get_xbit / set_xbit / set_zbit / rehash to transform
Pauli words.
Word-level vs. state-level gate semantics
The blanket Clifford impl applies gates at the bit level of a single
Pauli word. X / Y / Z are bit-level no-ops on a Pauli word — they affect
phase, not the X/Z bits — so word.x(i), word.y(i), word.z(i) are
deliberately silent. Phase is tracked separately by
PhasedPauliWord, which implements Clifford manually.
If you need a word representation whose gate behavior is not pure
bit manipulation (phase tracking, fused multi-qubit updates, alternative
loss semantics), do not implement PauliWordTrait on it — define a
specialized Clifford impl instead, the way PhasedPauliWord does.
Implementing both PauliWordTrait and a custom impl Clifford for ...
for the same type will not compile: the blanket impl overlaps with your
custom impl (coherence error), not silently shadows it.
ppvm-pauli-word
Packed Pauli strings: `PauliWord`, phased, lossy, and pattern variants.
A PauliWord-like type that additionally
tracks per-qubit loss.
Full docstring
A PauliWord-like type that additionally
tracks per-qubit loss.
In neutral-atom hardware (and in any loss-aware error model), a
measurement can return "lost" instead of 0 or 1. LossyPauliWord
adds a parallel lbits array marking which qubits have been lost; a
set loss bit excludes that qubit from the standard Pauli operations
and shows up as the Pauli::L symbol.
Examples
use ppvm_traits::char::Pauli;
use ppvm_pauli_word::loss::LossyPauliWord;
use ppvm_traits::traits::PauliWordTrait;
let w: LossyPauliWord<[u8; 1]> = "XLZL".into();
assert_eq!(w.weight(), 4); // X, L, Z, L are all non-identity
assert_eq!(w.loss_weight(), 2); // two qubits are lost
assert!(w.is(1, Pauli::L));
assert!(w.is(0, Pauli::X));
A fixed-width Pauli string stored as parallel x and z bit arrays.
Full docstring
A fixed-width Pauli string stored as parallel x and z bit arrays.
Each qubit slot is encoded by two bits, (x, z), giving the four
Pauli operators I = (0,0), X = (1,0), Z = (0,1), Y = (1,1) —
the same encoding stabilizer-formalism tools use. The bit arrays live
in a single PauliStorage blob (typically [u8; N] or [u64; N]),
so a PauliWord<[u8; 1]> packs up to 8 qubits, [u8; 2] up to 16,
etc.
A cached hash speeds up use as a map key; pass REHASH = false if
you build words in bulk and want to control rehashing manually.
Examples
use ppvm_traits::char::Pauli;
use ppvm_traits::traits::PauliWordTrait;
use ppvm_pauli_word::word::PauliWord;
// Build a word from a string …
let w: PauliWord<[u8; 1]> = "XYZI".into();
assert_eq!(w.n_qubits(), 4);
assert_eq!(w.get(0), Pauli::X);
assert_eq!(w.get(3), Pauli::I);
// … or build it slot-by-slot.
let mut w2: PauliWord<[u8; 1]> = PauliWord::new(2);
w2.set(0, Pauli::X).set(1, Pauli::Y);
assert_eq!(w2.to_string(), "XY");
assert_eq!(w2.weight(), 2);
A single-qubit Pauli that is not the identity: X, Y, or Z. Encoded so
the low two bits match Pauli (X = 1, Z = 2, Y = 3),
which lets From<NotIdentity> for Pauli be a no-op transmute.
Implemented by Pauli patterns over symbols (Contains<Pauli>) and
over whole words (Contains<W: PauliIter>); used by the masking
machinery to ask "does this pattern accept this concrete word?"
ppvm-pauli-sum
The `PauliSum` engine, truncation strategies, and concrete config bundles.
Pre-built configs backed by dashmap::DashMap for concurrent access.
Native-only: dashmap pulls rayon, which needs OS threads, so this
module is unavailable on wasm32.
HashMap-backed Config with native-word ([usize; N]) storage and
FxHasher.
Full docstring
HashMap-backed Config with native-word ([usize; N]) storage and
FxHasher.
The storage element is usize, i.e. the target's native machine word:
u64 on 64-bit targets (identical layout and performance to a hardcoded
[u64; N]) and u32 on 32-bit targets such as wasm32. Using usize
rather than u64 keeps this config available on every target — bitvec
only implements BitStore for u64 on 64-bit pointer widths, but always
implements it for usize. (The Byte8 name refers to the 64-bit word
this config packs into on native targets.)
Drop terms whose coefficient magnitude falls below the given threshold.
Defaults to 1e-12.
Full docstring
Drop terms whose coefficient magnitude falls below the given threshold.
Defaults to 1e-12.
Examples
use ppvm_pauli_sum::prelude::*;
use ppvm_pauli_sum::strategy::CoefficientThreshold;
let strict = CoefficientThreshold(1e-6);
let mut state: PauliSum<config::indexmap::ByteFxHashF64<1, CoefficientThreshold>> =
PauliSum::builder().n_qubits(2).strategy(strict).build();
state += ("ZZ", 1.0);
state += ("XX", 1e-9); // below threshold
state.truncate();
assert_eq!(state.len(), 1); // the XX term was dropped
The capacity hint is the smaller of the two; truncation applies both
policies in order, so use this when you want, e.g.,
"drop by coefficient threshold and cap maximum Pauli weight".
The central data type of the Pauli-propagation backend. Keys are
PauliWord-shaped Pauli strings; values are
numeric coefficients. Generic over a Config that fixes the
concrete storage / hasher / coefficient / strategy.
Internally PauliSum holds two maps — a primary and an auxiliary —
and swaps between them during gate propagation to avoid repeated
allocations. The PauliSum::data / PauliSum::aux accessors
expose the current orientation; most callers will only ever touch
the primary side via the high-level gate / measurement traits.
Examples
Heisenberg-picture propagation of ZZ through the GHZ circuit:
use ppvm_pauli_sum::prelude::*;
let mut state: PauliSum<config::indexmap::ByteFxHashF64<1>> =
PauliSum::builder().n_qubits(2).build();
state += ("ZZ", 1.0);
// Circuit: H(0); CNOT(0, 1) — apply in reverse for Heisenberg propagation.
state.cnot(0, 1);
state.h(0);
// ZZ → IZ under the GHZ circuit, with coefficient 1.0.
assert_eq!(state.len(), 1);
Builder's type state specifies if members are set or not (unset).
Full docstring
Builder's type state specifies if members are set or not (unset).
You can use the associated types of this trait to control the state of individual members with the [IsSet] and [IsUnset] traits. You can change the state of the members with the Set* structs available in this module.
type aliasByteFxHashF64ppvm_pauli_sum::config::dashmap::ByteFxHashF64
Apply the configured truncation Strategy
to the primary map, dropping entries that fall outside its policy.
Full docstring
Apply the configured truncation Strategy
to the primary map, dropping entries that fall outside its policy.
If preserve_strings is non-empty, any of those Pauli strings
that the strategy would have dropped are re-inserted afterwards
with their pre-truncate coefficient. The mechanism composes with
any Strategy (coefficient-magnitude, max-weight, combinations
— anything) because the strategy runs unchanged in the middle.
Implement multiplication by a scalar coefficient for PauliSum.
Use this macro to implement MulAssign for other scalar types
if needed. This macro will forward the multiplication to the underlying map's
mul_assign method.
Generalized stabilizer-tableau simulator built on top of the ppvm core
crates (ppvm-traits and ppvm-pauli-word).
Full docstring
Generalized stabilizer-tableau simulator built on top of the ppvm core
crates (ppvm-traits and ppvm-pauli-word).
Provides a forward-evolving state representation using the stabilizer
formalism, extended to non-Clifford gates by tracking a sparse vector
of coefficients indexed over bitstrings. The two top-level types are
data::Tableau (pure Clifford) and data::GeneralizedTableau
(Clifford + non-Clifford with sparse coefficient tracking).
Quick example
Prepare a Bell pair and verify the two measurements are perfectly
correlated:
use ppvm_tableau::prelude::*;
use ppvm_pauli_sum::config::fxhash::ByteF64;
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);
Pauli-string expectation values and pattern traces.
Pauli-string expectation values for GeneralizedTableau.
Full docstring
Pauli-string expectation values and pattern traces.
Pauli-string expectation values for GeneralizedTableau.
Two entry points:
[GeneralizedTableau::expectation] — single-Pauli ⟨ψ|P|ψ⟩ for a
PauliWord. Conjugates P through the tableau and overlaps the
resulting Pauli with the sparse coefficient vector using the same
formulas as the measurement code.
[GeneralizedTableau::trace] — Σ_{P matches pattern} ⟨ψ|P|ψ⟩ for a
PauliPattern. Enumerates the matching Paulis and sums their
expectations.
Decision 9 in the multi-backend plan calls these out as the natural
primitive for the tableau backend; semantics intentionally diverge from
the PauliSum trace.
TableauLike — shared trait for
stabilizer-tableau backends.
TableauLike trait: shared interface for stabilizer-tableau backends.
Full docstring
TableauLike — shared trait for
stabilizer-tableau backends.
TableauLike trait: shared interface for stabilizer-tableau backends.
Any type implementing TableauLike gets default implementations of
single- and two-qubit Pauli noise channels (Depolarizing, PauliError,
Depolarizing2, TwoQubitPauliError). New tableau backends only need to
provide TableauLike::rng_mut and (optionally) override
TableauLike::is_qubit_lost.
A Tableau extended with sparse coefficient tracking to handle
non-Clifford gates.
Full docstring
A Tableau extended with sparse coefficient tracking to handle
non-Clifford gates.
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
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.
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
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);
Per-measurement scratch buffers, reused across qubits within a single
measure_all invocation — and, when threaded through
measure_all_with_scratch,
across many shots of a sampler too. R…
Full docstring
Per-measurement scratch buffers, reused across qubits within a single
measure_all invocation — and, when threaded through
measure_all_with_scratch,
across many shots of a sampler too. Reusing one scratch keeps the case-a
HashMap and the b-entries Vec out of the per-shot allocator churn.
odd_phase_mask is lazily computed and cached until the destabilizers
change (i.e. until a case-a measurement runs update_tableau_according_to_outcome).
coeff_map is the case-a HashMap holding (idx → amplitude) between
the overlap, partition, and merge passes.
b_entries is the case-a partition's "k-bit = 1" scratch Vec.
Construct one per active sampling thread; the type is not meant to be
shared across threads concurrently.
A sparse vector keyed by index type I with values of type T.
Full docstring
A sparse vector keyed by index type I with values of type T.
Used by GeneralizedTableau to
store the coefficient over each branching bitstring. The default
implementation is Vec<(T, I)>; alternative backings (BTreeMap,
HashMap, etc.) can be provided by downstream code.
A stabilizer-tableau-like backend that supports Clifford gates and an RNG.
Full docstring
A stabilizer-tableau-like backend that supports Clifford gates and an RNG.
Implementing this trait grants default implementations of the Pauli noise
channels via TableauLike::depolarize_impl and friends. The associated
Rng type lets each backend choose its own RNG; nothing in this trait
depends on SmallRng.
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.
Full docstring
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,
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.
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, or
if the pairs' supports overlap (offset < count, including the
degenerate `offs…
Full docstring
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, or
if the pairs' supports overlap (offset < count, including the
degenerate offset == 0), which the fused kernel does not handle — see
Tableau::cz_block_pairs's preconditions.
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).
Full docstring
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.
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.
Full docstring
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
offset >= count, i.e. the pairs have 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 ORs the two shifted
planes, so a z bit written by two pairs is set once instead of
XOR-cancelled. Callers that cannot guarantee it must use
GeneralizedTableau::cz_block_pairs or
GeneralizedTableau::cz_block, which fall back to the per-pair loop.
Panics
Debug-asserts that all bits are within the same word and that the pairs
have pairwise-disjoint supports.
true iff p is an admissible correlated-loss parameter triple.
Full docstring
true iff p is an admissible correlated-loss parameter triple.
The channel is completely positive exactly when every event weight is
nonnegative, i.e. p0, p1 >= 0, p0 + 2·p1 <= 1 (p1 is the probability
that a named one of the pair is lost, so the exactly-one event carries
2·p1) and p2 ∈ [0, 1]. Outside that region the mixture truncates a
negative survivor weight and renormalizes, and the trajectory's cumulative
scan stops being a categorical sampler — both silently.
Coefficients that do not convert to f64 (symbolic ones) are not checked.
ppvm-sym provides a Term type that represents a polynomial in
sines and cosines of symbolic parameters. It is used by ppvm to
propagate Pauli operators through parametric circuits (e.g.,
variational ansätze) without committing to specific angle values
until the very end.
Quick example
Build the symbolic expression sin(x0) * cos(x1), then evaluate it
at a concrete (x0, x1):
use ppvm_sym::Term;
let expr = Term::var(0).sin() * Term::var(1).cos();
let v = expr.eval(&[0.5, 1.0]).unwrap();
let expected = 0.5_f64.sin() * 1.0_f64.cos();
assert!((v - expected).abs() < 1e-12);
A single product of trigonometric atoms over symbolic variables.
Sines and cosines are grouped by variable id; powers are tracked as
totals so we can quickly compute and bound them. The order of
variables is kept canonical (ascending) so two Prod values
representing the same monomial compare equal.
Term is the public-facing wrapper around its Inner enum. It
also carries two truncation parameters, applied during multiplication
and addition:
Examples
use ppvm_sym::Term;
// sin²(x0) at x0 = π/2 equals 1.
let expr = Term::var(0).sin() * Term::var(0).sin();
let v = expr.eval(&[std::f64::consts::FRAC_PI_2]).unwrap();
assert!((v - 1.0).abs() < 1e-12);
max_sin — drop terms whose total sine power exceeds this bound.
min_eps — drop terms whose coefficient magnitude falls below
this threshold.
Sum holds a full formal sum; One is the optimisation for a
single weighted product; Var is a bare symbolic variable used
before it has been wrapped in a sin or cos; Const is a
numeric scalar.