Source code for pprop.propagator.evolve

"""This module handles the core evolution of Pauli words through a list of gates
via the Heisenberg picture.
"""
from typing import List, Optional, Tuple

from ..gates.base import Gate
from ..pauli.sentence import CoeffTerms, PauliDict
from .pruning import DeadQubitPruner


[docs] def to_expectation(paulidict: PauliDict) -> CoeffTerms: r""" Extract the expectation value expression from a propagated :class:`~pprop.pauli.sentence.PauliDict`. In the :math:`|0\rangle^{\otimes n}` computational basis state, only Pauli words composed entirely of :math:`Z` and :math:`I` operators have non-zero expectation: .. math:: \langle 0 | I | 0 \rangle = 1, \quad \langle 0 | Z | 0 \rangle = 1, \quad \langle 0 | X | 0 \rangle = 0, \quad \langle 0 | Y | 0 \rangle = 0 This function iterates over all Pauli words in ``paulidict``, keeps only those that satisfy the zero-bracket condition (i.e. only :math:`Z`/:math:`I` on every qubit), and concatenates their :data:`CoeffTerms` into a single flat list representing the full expectation value expression. Parameters ---------- paulidict : PauliDict Mapping of ``PauliOp -> CoeffTerms`` after Heisenberg evolution. Returns ------- CoeffTerms Flat list of :data:`CoeffTerm` tuples whose sum gives the expectation value :math:`\langle 0 | O | 0 \rangle`. """ expr: CoeffTerms = [] for pauliword, coeffterms in paulidict.items(): # Only Z and I operators have non-zero expectation in the |0⟩ state. if pauliword.zerobracket(): expr += coeffterms # coeffterms is a CoeffTerms (list), so += extends the list return expr
[docs] def heisenberg( gates : List[Gate], paulidict: PauliDict, k1: Optional[int], k2: Optional[int], opt: bool = False, debug: bool = False, ) -> Tuple[PauliDict, CoeffTerms]: r""" Evolve a :class:`~pprop.pauli.sentence.PauliDict` backwards through a list of gates (Heisenberg picture). Each gate is applied in *reverse* order so that the observable is propagated from the measurement end of the circuit back to the input. After all gates have been applied, :func:`to_expectation` extracts the symbolic expectation value expression as a :data:`CoeffTerms` list. Parameters ---------- gates : list[pprop.gates.Gate] Ordered list of gates as they appear in the circuit (will be iterated in reverse). paulidict : PauliDict Initial observable represented as a mapping of ``PauliOp -> CoeffTerms``. k1 : int or None Pauli weight cutoff. Evolved terms whose Pauli weight exceeds ``k1`` are discarded. ``None`` disables this truncation. k2 : int or None Frequency cutoff. Evolved terms whose total trigonometric frequency exceeds ``k2`` are discarded. ``None`` disables this truncation. opt : bool, optional If ``True``, use optimized pruning strategy. Defaults to ``False``. debug : bool, optional If ``True``, print the gate, pre-evolution, and post-evolution state at each step. Defaults to ``False``. Returns ------- paulidict : PauliDict The fully evolved observable after all gates have been applied. expectation : CoeffTerms Flat list of :data:`CoeffTerm` tuples encoding the symbolic expectation value :math:`\langle 0 | U^\dagger O U | 0 \rangle`. """ reversed_gates = gates[::-1] if opt: # pruners = [DeadQubitPruner(), XYWeightPruner()] pruners = [DeadQubitPruner()] for pruner in pruners: pruner.setup(reversed_gates) for i, gate in enumerate(reversed_gates): if opt: for pruner in pruners: pruner.prune(paulidict, i) pauli_add = PauliDict() # Evolved replacement terms to add pauli_remove = PauliDict() # Original terms to remove after evolution for pauliword, coeffterms in paulidict.items(): # Evolve this (pauliword, coeffterms) pair through the gate. evolved: PauliDict = gate.evolve((pauliword, coeffterms), k1, k2) pauli_add += evolved # Only the key (PauliOp) matters here; the coefficient is irrelevant # because we are removing the entire entry from paulidict. pauli_remove[pauliword] = [] if debug: print("=== Evolve ===") print("GATE:", gate) print(" PRE:", paulidict) # Swap out the original terms for their evolved counterparts. paulidict -= pauli_remove paulidict += pauli_add if debug: print(" REM:", pauli_remove) print(" ADD:", pauli_add) print("POST:", paulidict) return paulidict, to_expectation(paulidict)