tensorcircuit.pauliprop¶
Pauli Propagation Engine¶
This module implements a Pauli Propagation Engine (PPE) that tracks the evolution of Pauli observables through a quantum circuit using a global k-local representation.
- Key Features:
Tracks all k-local Pauli strings globally, avoiding subset boundary issues.
Uses precomputed Neighbor Maps for O(1) fiber lookups.
JAX JIT and AD compatible.
Naturally handles truncation of terms exceeding locality k.
- class tensorcircuit.pauliprop.PauliPropagationEngine(N: int, k: int)[source]¶
Bases:
objectA Pauli Propagation Engine that tracks observables in the global k-local space.
The state is represented as a flat vector of size $|P_k| = sum_{i=0}^k binom{N}{i} 3^i$.
Note
The engine internally uses single precision (
complex64) for state coefficients. Since Pauli propagation is an approximate algorithm, the systematic errors introduced by truncation are typically much larger than the numerical errors from single-precision floating point.- __init__(N: int, k: int) None[source]¶
Initialize the Pauli Propagation Engine.
- Parameters:
N (int) – Total number of qubits.
k (int) – Maximum locality of the Pauli strings to track.
- apply_gate(state: Any, gate_name: str, wires: Any, params: Any = None) Any[source]¶
Propagate the observable through a quantum gate in the Heisenberg picture. Applies $O to U^dagger O U$.
- Parameters:
state (Any) – Current observable state vector.
gate_name (str) – Name of the gate (e.g., ‘rx’, ‘cnot’).
wires (Any) – List of qubit indices the gate acts on.
params (Any) – Optional gate parameters.
- Returns:
Updated observable state vector.
- Return type:
Any
- compute_expectation_scan(ham_structures: Any, ham_weights: Any, layer_fn: Callable[[...], None], params_batch: Any, extra_inputs: Sequence[Any] | None = None) Any[source]¶
Compute expectation value with JAX scan optimization for layers.
- Parameters:
ham_structures (Any) – Hamiltonian structure array.
ham_weights (Any) – Hamiltonian weights.
layer_fn (Callable) – A function f(circuit, params, *extra_inputs) that defines a circuit layer.
params_batch (Any) – Batched parameters for the layers.
extra_inputs (Optional[Sequence[Any]]) – Optional static inputs for the layer function.
- Returns:
Final expectation value.
- Return type:
Any
- expectation(state: Any) Any[source]¶
Compute the expectation value $langle 0| O(t) |0 rangle$. Sum coefficients of purely Z observables in the final state.
- Parameters:
state (Any) – Propagated observable state vector.
- Returns:
Real-valued expectation value.
- Return type:
Any
- get_initial_state(structures: Any, weights: Any) Any[source]¶
Initialize the state vector in the k-local Hilbert space from Hamiltonian terms.
- Parameters:
structures (Any) – Hamiltonian structure array of shape [n_terms, n_qubits]. Each entry is 0:I, 1:X, 2:Y, 3:Z.
weights (Any) – Coefficients for each Hamiltonian term.
- Returns:
Initial state vector of size dim+1 (including sink).
- Return type:
Any
# Initialize Z0 + Z1 for a 2-qubit system engine = PauliPropagationEngine(N=2, k=2) structures = [[3, 0], [0, 3]] weights = [1.0, 1.0] state = engine.get_initial_state(structures, weights)
- string_to_code(s: Tuple[Tuple[int, ...], Tuple[int, ...]]) int[source]¶
Convert a Pauli string representation ((qidx, …), (opcode, …)) to its index in the basis.
- Parameters:
s (Tuple[Tuple[int, ...], Tuple[int, ...]]) – Pauli string as ((qidx, …), (opcode, …)).
- Returns:
Index in the basis.
- Return type:
int
- class tensorcircuit.pauliprop.SparsePauliPropagationEngine(N: int, k: int, buffer_size: int = 2000)[source]¶
Bases:
objectA Truly Sparse Pauli Propagation Engine that tracks Pauli strings using bitpacked integers. No combinatorial basis is precomputed, making it suitable for hundreds of qubits.
Note
The engine internally uses single precision (
complex64) for coefficients, but utilizesint64bit-packing for encoding Pauli strings. It is highly recommended to calltc.set_dtype("complex128")when using this engine. While the coefficients remain single-precision, this setting ensures the backend (especially JAX) enables native 64-bit support, which is essential for theint64encoding and bitwise operations used in the large-scale simulation.Note
The numerical precision error of
complex64is significantly smaller than the systematic approximation error from buffer truncation, making double-precision coefficients practically unnecessary for this engine.- apply_gate(state: Any, gate_name: str, wires: Any, params: Any = None) Any[source]¶
Propagate the observable through a quantum gate in the Heisenberg picture. Applies $O to U^dagger O U$.
- Parameters:
state (Any) – Current sparse observable state (codes, weights).
gate_name (str) – Name of the gate.
wires (Any) – List of qubit indices the gate acts on.
params (Any) – Optional gate parameters.
- Returns:
Updated sparse observable state (codes, weights).
- Return type:
Any
- compute_expectation_scan(structures: Any, weights: Any, layer: Callable[[Any, Any], None], params: Any) Any[source]¶
Compute expectation value with JAX scan optimization for layers.
- Parameters:
structures (Any) – Initial Hamiltonian structures.
weights (Any) – Initial Hamiltonian weights.
layer (Callable) – A function f(circuit, params) that defines a circuit layer.
params (Any) – Batched parameters for the layers.
- Returns:
Final expectation value.
- Return type:
Any
- expectation(state: Any) Any[source]¶
Compute the expectation value $langle 0| O(t) |0 rangle$. Sum coefficients of purely Z observables in the final state.
- Parameters:
state (Any) – Propagated sparse observable state (codes, weights).
- Returns:
Real-valued expectation value.
- Return type:
Any
- get_initial_state(structures: Any, weights: Any) Any[source]¶
Initialize the sparse state (codes, coefficients).
- Parameters:
structures (Any) – Hamiltonian structures [n_terms, n_qubits].
weights (Any) – Hamiltonian coefficients.
- Returns:
A tuple of (codes, weights) representing the sparse state.
- Return type:
Any
# Initialize Z0 + Z1 for a 100-qubit system engine = SparsePauliPropagationEngine(N=100, k=2, buffer_size=1000) structures = np.zeros((2, 100), dtype=int) structures[0, 0] = 3 # Z structures[1, 1] = 3 # Z weights = [1.0, 1.0] state = engine.get_initial_state(structures, weights)
- string_to_code(s: Tuple[Tuple[int, ...], Tuple[int, ...]]) Any[source]¶
Convert a Pauli string representation ((qidx, …), (opcode, …)) to its bit-packed int64 representation.
- Parameters:
s (Tuple[Tuple[int, ...], Tuple[int, ...]]) – Pauli string as ((qidx, …), (opcode, …)).
- Returns:
Bit-packed int64 tensor.
- Return type:
Any
- tensorcircuit.pauliprop.pauli_propagation(c: Circuit, observable: Any, weights: Any | None = None, k: int = 3) Any[source]¶
High-level API for Heisenberg-picture Pauli propagation.
- Parameters:
c (Circuit) – The quantum circuit to propagate through.
observable (Any) – The initial observable. Can be: 1. A list of (coeff, pauli_string) pairs, e.g.,
[(1.0, "ZZ"), (-0.5, "XI")]. 2. A structure array ifweightsis also provided.weights (Optional[Any]) – Optional weights if
observableis a structure array.k (int) – Maximum locality to track. Defaults to 3.
- Returns:
Real-valued expectation value $langle 0 | U^dagger O U | 0 rangle$.
- Return type:
Any
import tensorcircuit as tc from tensorcircuit.pauliprop import pauli_propagation c = tc.Circuit(2) c.h(0) c.cnot(0, 1) # Compute expectation of Z0 + Z1 obs = [(1.0, "ZI"), (1.0, "IZ")] energy = pauli_propagation(c, obs, k=2)