Core concepts¶
The Heisenberg picture¶
For a circuit \(C\), an observable \( \mathcal{O}\) and an initial state \(|\Psi_0\rangle\), we often want to compute an expectation value:
A state-vector, or density matrix simulator evolves \( \rho_0 = |\Psi_0\rangle \langle \Psi_0 |\) forward. The cyclicity of trace permits us to rewrite this as
i.e. we evolve the observable \(\mathcal{O}\) backward through the circuit, and compute the overlap with the initial state (usually a bitstring, or a Fock state in the fermionic case). This is the Heisenberg picture, and it is the basis of propaq's approach.
Unlike the Schrödinger picture where we require exponential space just to materialize the state vector, in the Heisenberg picture we only need to keep track of the observable, which is often sparse in the Pauli or Majorana basis, both of which are known to have compact bit representations due to their symplectic structure. The challenge is that non-Clifford gates cause the observable to branch into multiple terms during the backpropagation, so the number of terms grows exponentially, as one would expect from the classical simulation of a quantum circuit. Truncation and noise are the two mechanisms that propaq provides to manage this growth, and they are discussed in the truncation guide and the noise guide.
Terms, term sums and branching¶
\(\Theta := C^\dagger \mathcal{O} C\) is represented as a term sum:
- In the Pauli basis, the terms are Pauli strings:
PauliString, collected into aPauliTermSum. - In the Majorana basis, the terms are Majorana monomials:
MajoranaMonomial, collected into aMajoranaTermSum.
Other bases?
Heisenberg propagation can be carried out in any basis with the right algebraic structure. For example, in a Weyl-Heisenberg qudit basis, the terms could be WeylString, collected into a WeylTermSum.
See this notebook for an example of a custom basis.
Applying a gate rewrites every term. A Clifford gate maps one basis operator to exactly one basis operator, so the term count is unchanged. A non-Clifford gate will, in general, induce branching:
and as a result, over a deep circuit the number of terms will inevitably grow out of control. Therefore, we need pruning mechanisms to keep the term count manageable.
Merging (lossless). Distinct branches frequently arrive at the same basis operator, and their coefficients can simply be summed.
Truncation (lossy). Terms that are unlikely to matter are discarded, either by operator weight, by coefficient magnitude, or by capping the total term count. See the truncation guide.
Pauli versus Majorana¶
propaq propagates in either basis. They are related by the Jordan–Wigner transform, but they are not equally efficient for the same problem.
| Pauli | Majorana | |
|---|---|---|
| Basis elements | Pauli strings over \(n\) qubits | products of Majorana operators over \(2n\) modes |
| Natural for | qubit circuits, spin models, general ansätze | fermionic circuits (chemistry, Hubbard, UCJ ansätze) |
| Circuit type | PauliCircuit |
MajoranaCircuit |
| Propagator | PauliPropagator |
MajoranaPropagator |
Notebook 02 runs the same UCJ ansatz both ways and compares them directly.
Noise¶
A noise model damps terms as they propagate, which both models a noisy device
and accelerates the simulation by shrinking coefficients below the truncation
cutoff sooner. The built-in
UniformNoiseModel is uniform-depolarizing noise, and scales a term of weight
\(w\) by \(e^{-\gamma w}\); you can also supply a Python model, or load a native
one written in C, Rust or Julia. See the noise guide.
Surrogate (symbolic) propagation¶
If the circuit is parameterized, repeating the whole propagation for every parameter assignment is wasteful, because the branching structure does not depend on the parameter values, only the coefficients do.
The surrogate propagators exploit this. They back-propagate once, keeping
each coefficient as a symbolic expression in the circuit parameters, and produce
a compiled PauliSurrogateModel or
MajoranaSurrogateModel. Evaluating that
model for a given parameter vector is a cheap calculation, and the
model can be saved to disk and reloaded. See the
surrogate guide.
Surrogate propagation is expensive.
Surrogate propagation is more expensive than numerical propagation, since we need to maintain symbolic coefficients, whose merge semantics are more restrictive. Additionally, this part of propaq is still under active development, and is subject to change.
The object model at a glance¶
flowchart LR
QC["Qiskit / Cirq<br/>QuantumCircuit"] --> C["PauliCircuit<br/>MajoranaCircuit"]
SPO["SparsePauliOp<br/>fermionic operator"] --> TS["PauliTermSum<br/>MajoranaTermSum"]
C --> P["Propagator"]
TS --> P
N["NoiseModel"] --> P
T["Truncator pipeline"] --> P
P --> R["PropagationResult<br/>(expectation_value)"]
P --> TH["propagated TermSum"]
QCP["parameterized circuit"] --> SC["SurrogatePauliCircuit<br/>SurrogateMajoranaCircuit"]
SC --> SP["SurrogatePropagator"]
TS --> SP
SP --> M["SurrogateModel<br/>evaluate(params)"]