pauli_propagation

Dependencies

Included in the core package — no additional dependencies required.

Executor

class qc_executor.pauli_propagation.PauliPropagationExecutor(shots=None, seed=None, log_file=None, log_level='WARNING', caching=None, cache_dir='cache', max_cache_size=None, truncate_threshold=None, max_weight=None, symmetry_strategy=None, n_jobs=1)[source]

Bases: ExecutorBase

Executor for quantum circuits using Pauli propagation (Heisenberg picture).

This executor propagates observables backward through quantum circuits, enabling efficient computation of expectation values for sparse observables.

Parameters:
  • shots (int | None) – Number of measurement shots (not used for exact simulation)

  • seed (int | None) – Random seed for reproducibility (not used for exact simulation)

  • log_file (str | None) – Path to log file (not implemented)

  • caching (bool | None) – Whether to use caching (not implemented yet)

  • cache_dir (str) – Directory for caching (not implemented yet)

  • truncate_threshold (float | None) – Coefficient threshold for automatic truncation (None = no truncation)

  • max_weight (int | None) – Maximum Pauli weight for truncation (None = no weight limit)

  • symmetry_strategy (SymmetryStrategy | None) – Strategy for Pauli symmetry merging (None = no merging)

  • n_jobs (int) – Number of worker processes for independent expectation-value evaluations (multiple circuits x observables, parameter-shift gradients). 1 (default) runs serially; -1 uses all CPU cores. Process startup on Windows (spawn) costs roughly 100 ms per worker plus import time, so parallelism pays off only for workloads with many or expensive independent evaluations.

  • log_level (str)

  • max_cache_size (int | None)

property shots: int | None

Return the number of shots.

property remote: bool

Return False (Pauli propagation is local execution).

transpile_operator(operator, symmetry_strategy=None)[source]
Overloads:
  • self, operator (QuantumOperatorBase) → PauliPropagationOperator

  • self, operator (QuantumOperatorBase), symmetry_strategy (SymmetryStrategy) → PauliPropagationOperator

  • self, operator (List[QuantumOperatorBase]) → List[PauliPropagationOperator]

  • self, operator (List[QuantumOperatorBase]), symmetry_strategy (SymmetryStrategy) → List[PauliPropagationOperator]

Parameters:
  • operator (QuantumOperatorBase | List[QuantumOperatorBase])

  • symmetry_strategy (SymmetryStrategy | None)

Return type:

PauliPropagationOperator | List[PauliPropagationOperator]

Transpile the operator for execution on Pauli Propagation backend.

Accepts both native PauliPropagationOperator and generic QuantumOperator types. When a list of operators is provided, each operator is transpiled and cached individually.

Parameters:
  • operator (QuantumOperatorBase | List[QuantumOperatorBase]) – The quantum operator or a list of operators to transpile.

  • symmetry_strategy (SymmetryStrategy | None) – Strategy for symmetry handling. If provided, takes precedence over executor-level default.

Returns:

The

transpiled operator(s) in native format.

Return type:

PauliPropagationOperator | List[PauliPropagationOperator]

get_truncation_stats()[source]

Get statistics from last truncation operation.

Return type:

TruncationStats | None

Returns:

TruncationStats from most recent execution, or None if no truncation

classmethod get_accepted_backend_types()[source]

Return all object types accepted as backend in factory auto-detection.

PauliPropagationExecutor does not accept backend objects during initialization.

Return type:

List[type]

classmethod get_accepted_backend_aliases()[source]

Return string aliases accepted by this executor in Executor.create.

Return type:

List[str]

Native abstraction

Circuit

class qc_executor.pauli_propagation.PauliPropagationCircuit(num_qubits, *, gates=None, parameter_symbols=None)[source]

Bases: QuantumCircuitBase

Backend-native circuit representation for Pauli propagation.

This datatype stores operations directly in the internal gate representation used by the propagation engine and does not depend on Qiskit objects.

Parameters:
  • num_qubits (int)

  • gates (Sequence[Gate | LayerBarrier] | None)

  • parameter_symbols (Dict[str, sp.Symbol] | None)

classmethod from_quantum_circuit(circuit)[source]

Create a PauliPropagationCircuit from a generic circuit.

Return type:

PauliPropagationCircuit

Parameters:

circuit (QuantumCircuitBase)

property gates: List[Gate | LayerBarrier]

Return a shallow copy of gate instructions.

property parameters: List[str]

Return parameter names used by the circuit.

property parameter_symbols: Dict[str, Symbol]

Return a copy of the parameter-name to sympy-symbol mapping.

property num_parameters: int

Return the number of free trainable parameters in the circuit.

property is_parameterized: bool

Check if the wavefunction is parameterized.

draw()[source]

Returns printable string representation of the circuit.

Return type:

str

h(qubits)[source]

Add hadamard gates

Parameters:

qubits (int | List[int])

s(qubits)[source]

Add S gates

Parameters:

qubits (int | List[int])

sdag(qubits)[source]

Add Sdag gates

Parameters:

qubits (int | List[int])

t(qubits)[source]

Add T gates

Parameters:

qubits (int | List[int])

tdag(qubits)[source]

Add Tdg gates

Parameters:

qubits (int | List[int])

p(qubits, angle)[source]

Add P gates

Parameters:
  • qubits (int | List[int])

  • angle (float)

x(qubits)[source]

Add X gates

Parameters:

qubits (int | List[int])

y(qubits)[source]

Add Y gates

Parameters:

qubits (int | List[int])

z(qubits)[source]

Add Z gates

Parameters:

qubits (int | List[int])

rx(qubits, angle)[source]

Add RX gates

Parameters:
  • qubits (int | List[int])

  • angle (float)

ry(qubits, angle)[source]

Add RY gates

Parameters:
  • qubits (int | List[int])

  • angle (float)

rz(qubits, angle)[source]

Add RZ gates

Parameters:
  • qubits (int | List[int])

  • angle (float)

cx(control_qubit, target_qubit)[source]

Add CNOT gates

Parameters:
  • control_qubit (int)

  • target_qubit (int)

cy(control_qubit, target_qubit)[source]

Add CY gates

Parameters:
  • control_qubit (int)

  • target_qubit (int)

cz(control_qubit, target_qubit)[source]

Add CZ gates

Parameters:
  • control_qubit (int)

  • target_qubit (int)

crx(control_qubit, target_qubit, angle)[source]

Add CRX gates

Parameters:
  • control_qubit (int)

  • target_qubit (int)

  • angle (float)

cry(control_qubit, target_qubit, angle)[source]

Add CRX gates

Parameters:
  • control_qubit (int)

  • target_qubit (int)

  • angle (float)

crz(control_qubit, target_qubit, angle)[source]

Add CRX gates

Parameters:
  • control_qubit (int)

  • target_qubit (int)

  • angle (float)

rxx(control_qubit, target_qubit, angle)[source]

Add RXX gates

Parameters:
  • control_qubit (int)

  • target_qubit (int)

  • angle (float)

ryy(control_qubit, target_qubit, angle)[source]

Add RYY gates

Parameters:
  • control_qubit (int)

  • target_qubit (int)

  • angle (float)

rzz(control_qubit, target_qubit, angle)[source]

Add RZZ gates

Parameters:
  • control_qubit (int)

  • target_qubit (int)

  • angle (float)

rzx(control_qubit, target_qubit, angle)[source]

Add RZX gates

Parameters:
  • control_qubit (int)

  • target_qubit (int)

  • angle (float)

swap(qubit1, qubit2)[source]

Add SWAP gates

Parameters:
  • qubit1 (int)

  • qubit2 (int)

barrier(qubits)[source]

Add barrier gates

Parameters:

qubits (int | List[int])

measure()[source]

Add measure gates

compose(qc, qubits)[source]

Compose two quantum circuits.

Return type:

PauliPropagationCircuit

Parameters:
  • qc (QuantumCircuitBase)

  • qubits (List[int])

assign_parameters(parameters)[source]

Bind symbolic parameters to concrete values.

Parameters:

parameters (Dict[str, float]) – Dict mapping parameter names to float values

Return type:

PauliPropagationCircuit

Returns:

New circuit with parameters substituted

invert()[source]

Invert the circuit.

Return type:

PauliPropagationCircuit

copy()[source]

Return a copy of the circuit.

Return type:

PauliPropagationCircuit

replace_gate(index, gate)[source]

Return a new circuit with the gate at index replaced.

Unreplaced gate objects are shared with this circuit; gates are immutable after construction, so this is safe.

Return type:

PauliPropagationCircuit

Parameters:
  • index (int)

  • gate (Gate | LayerBarrier)

circuit_metrics()[source]

count number of gates in the circuit

Return type:

dict

from_qasm(qasm)[source]

Load the circuit from a qasm string

Return type:

None

Parameters:

qasm (str)

to_qasm()[source]

Convert the circuit to a qasm string

Return type:

str

Operator

class qc_executor.pauli_propagation.PauliPropagationOperator(paulis=None, coeffs=None, num_qubits=None, pauli_sum=None, symmetry_strategy=None, *, parametric_coeffs=None, parameter_symbols=None)[source]

Bases: QuantumOperatorBase

Backend-native operator representation for Pauli propagation.

Parameters:
  • paulis (List[str] | None)

  • coeffs (List[complex | 'sp.Expr'] | None)

  • num_qubits (int | None)

  • pauli_sum (PauliSum | None)

  • symmetry_strategy (SymmetryStrategy | None)

  • parametric_coeffs (Dict[int, sp.Expr] | None)

  • parameter_symbols (Dict[str, sp.Symbol] | None)

classmethod from_quantum_operator(operator, symmetry_strategy=None)[source]
Overloads:
  • cls, operator (QuantumOperatorBase) → PauliPropagationOperator

  • cls, operator (QuantumOperatorBase), symmetry_strategy (SymmetryStrategy) → PauliPropagationOperator

Parameters:
Return type:

PauliPropagationOperator

Create a PauliPropagationOperator from a generic operator.

property pauli_sum: PauliSum

Return a copy of the underlying PauliSum.

property symmetry

Return the symmetry strategy of the underlying PauliSum.

property has_active_symmetry: bool

Return True if a non-trivial symmetry strategy is active.

property num_qubits: int

Return the number of qubits in the circuit.

property num_paulis: int

Return the number of Paulis in the operator.

property paulis: List[str]

Return the list of Paulis.

property coeffs: List[complex]

Return the list of coefficients.

property is_parametrized: bool

Return True if the operator is parametrized.

property parameters: List[str]

Return the parameters of the operator.

Returns:

List of parameters.

property parameter_symbols: Dict[str, Symbol]

Return a copy of the parameter-name to sympy-symbol mapping.

property parametric_coeffs: Dict[int, Expr]

Return a copy of the term to symbolic-coefficient mapping.

property num_parameters: int

Return the number of parameters in the operator.

Returns:

Number of parameters.

copy()[source]

Return a deep copy of this operator.

Return type:

PauliPropagationOperator

assign_parameters(parameters)[source]

Bind symbolic parameters to concrete values.

Parameters:

parameters (Dict[str, float]) – Dict mapping parameter names to float values

Return type:

PauliPropagationOperator

Returns:

New operator with parameters substituted

adjoint()[source]

Return the adjoint of the operator.

Return type:

PauliPropagationOperator

Returns:

Adjoint of the operator.

apply_layout(layout)[source]

Apply a layout to the operator.

Parameters:

layout (List[int]) – Layout to apply.

Return type:

PauliPropagationOperator

Returns:

Operator with applied layout.

compose(other)[source]

Compose the operator with another operator.

Parameters:

other (QuantumOperatorBase) – Operator to compose with.

Return type:

PauliPropagationOperator

Returns:

Composed operator.

append(pauli, coeff=None)[source]

Append another operator to the current operator.

Parameters:
  • other (QuantumOperatorBase) – Operator to append.

  • pauli (str)

Return type:

PauliPropagationOperator

Returns:

Appended operator.

simplify()[source]

Simplify the operator.

Return type:

PauliPropagationOperator

Returns:

Simplified operator.

transpose()[source]

Return the transpose of the operator.

Return type:

PauliPropagationOperator

Returns:

Transpose of the operator.

conjugate()[source]

Return the conjugate of the operator.

Return type:

PauliPropagationOperator

Returns:

Conjugate of the operator.

group_commuting()[source]

Group commuting operators.

Return type:

List[PauliPropagationOperator]

Returns:

List of commuting operators.

property is_unitary: bool

Return True if the operator is unitary.

Returns:

True if the operator is unitary.

property is_real: bool

Return True if the operator is real.

Returns:

True if the operator is real.

property is_imaginary: bool

Return True if the operator is imaginary.

Returns:

True if the operator is imaginary.

Other classes

class qc_executor.pauli_propagation.SymmetryStrategy[source]

Bases: ABC

Abstract base class for symmetry strategies.

Symmetry strategies define how Pauli terms are grouped into equivalence classes. Each strategy must implement canonical_representative() to map a Pauli term to its canonical form.

Canonical Representative:

A unique representative element from each equivalence class under the symmetry transformation. Terms with the same canonical representative are considered equivalent and can be merged (coefficients summed).

Example

For qubit permutation symmetry, the strings XXY, XYX, YXX all have the same multiset {X, X, Y} and should map to the same canonical form.

Implementation Notes:
  • Pauli terms are encoded as integers (2 bits per qubit)

  • Encoding: I=00, X=01, Y=10, Z=11 (little-endian)

  • canonical_representative() must be deterministic

  • canonical_representative() should be fast (called many times)

abstractmethod canonical_representative(term, nqubits)[source]

Compute canonical representative of a Pauli term.

Maps a Pauli term to its canonical form under this symmetry. All terms in the same equivalence class must map to the same canonical.

Parameters:
  • term (int) – Pauli term encoded as integer (2 bits per qubit, little-endian)

  • nqubits (int) – Number of qubits

Return type:

int

Returns:

Canonical representative (integer, same encoding as input)

abstract property name: str

Return human-readable name of this symmetry strategy.

Used for debugging, logging, and statistics tracking.

class qc_executor.pauli_propagation.NoSymmetry[source]

Bases: SymmetryStrategy

Identity symmetry strategy (no merging).

This strategy performs no merging - each Pauli term is its own canonical representative. Used as the default symmetry when no specific symmetry is requested.

Useful for:
  • Backward compatibility (default behavior)

  • Baseline comparisons (measuring impact of symmetry merging)

  • Debugging (disable merging without changing code structure)

Performance:
  • O(1) per canonical call (identity function)

  • Zero computational overhead

canonical_representative(term, nqubits)[source]

Return term unchanged (identity function).

Parameters:
  • term (int) – Pauli term encoded as integer

  • nqubits (int) – Number of qubits (unused)

Return type:

int

Returns:

The input term unchanged

property name: str

Return ‘no_symmetry’ as identifier.

class qc_executor.pauli_propagation.PermutationSymmetry[source]

Bases: SymmetryStrategy

Qubit permutation symmetry (S_n) using sorted multiset canonical form.

Groups Pauli terms that differ only by qubit permutations. The canonical representative is the term with Paulis sorted lexicographically (I < X < Y < Z).

Mathematical Background:

Two Pauli strings P and Q are in the same orbit under S_n (symmetric group of n elements) if they have the same multiset of local Pauli operators.

Example: XXY, XYX, YXX all have multiset {X, X, Y} → same orbit

Implementation:

Uses O(n) bit manipulation without lookup tables. Canonical form is computed by counting occurrences of each Pauli type and reconstructing in sorted order.

Algorithm Complexity:

Time: O(n) per canonical computation (one pass to count, one to reconstruct) Space: O(1) (fixed-size counter array [4])

Scales efficiently to 100+ qubits.

Example

>>> sym = PermutationSymmetry()
>>> # Encode XXY, XYX, YXX at 3 qubits
>>> term1 = 0x05  # XXY binary: 010001
>>> term2 = 0x09  # XYX binary: 100001
>>> term3 = 0x06  # YXX binary: 000110
>>> sym.canonical_representative(term1, 3)  # All map to same value
>>> sym.canonical_representative(term2, 3)
>>> sym.canonical_representative(term3, 3)
canonical_representative(term, nqubits)[source]

Compute canonical Pauli term under qubit permutations.

The canonical form is reconstructed from the multiset of local Pauli symbols in sorted order I < X < Y < Z.

Uses whole-word popcounts to count Pauli types and repeated-pattern arithmetic to reconstruct the sorted form, so the cost is O(1) word operations instead of a per-qubit Python loop.

Parameters:
  • term (int) – Pauli term encoded as integer (2 bits per qubit, little-endian).

  • nqubits (int) – Number of qubits.

Return type:

int

Returns:

Canonical representative as integer.

property name: str

Return ‘permutation’ as identifier.

class qc_executor.pauli_propagation.CompositeSymmetry(*strategies)[source]

Bases: SymmetryStrategy

Compose multiple symmetry strategies.

Applies multiple symmetry strategies sequentially to compute the final canonical representative. The result of strategy i becomes the input to strategy i+1.

Use Cases:
  • Combine multiple independent symmetries (e.g., permutation + point group)

  • Layer symmetries with different granularities

  • Experimental symmetry compositions for research

Implementation:

Strategies are applied in the order they are provided to __init__(). For commuting symmetries, order doesn’t matter. For non-commuting symmetries, different orders may give different canonical forms.

Performance:

Time: Sum of individual strategy times Space: O(1) beyond storage of strategy references

Example

>>> perm_sym = PermutationSymmetry()
>>> point_group_sym = PointGroupSymmetry()  # hypothetical
>>> composite = CompositeSymmetry(perm_sym, point_group_sym)
>>>
>>> # Applies permutation symmetry first, then point group
>>> canonical = composite.canonical_representative(term, nqubits)

Notes

  • Empty CompositeSymmetry (no strategies) acts as NoSymmetry

  • Single strategy behaves identically to using that strategy alone

  • Ordering matters only if symmetries don’t commute

Parameters:

strategies (SymmetryStrategy)

canonical_representative(term, nqubits)[source]

Apply all configured strategies sequentially.

Parameters:
  • term (int) – Pauli term encoded as integer.

  • nqubits (int) – Number of qubits.

Return type:

int

Returns:

Canonical representative after all strategies are applied.

property name: str

Return composite name listing all strategies.

Format: ‘composite(strategy1 + strategy2 + …)’