Source code for qdk_chemistry.algorithms.controlled_circuit_mapper.controlled_pauli_sequence_mapper

"""QDK/Chemistry sequence structure controlled circuit mapper."""

# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See LICENSE.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from qdk import qsharp

from qdk_chemistry.data.circuit import Circuit, QsharpFactoryData
from qdk_chemistry.data.unitary_representation.base import UnitaryRepresentation
from qdk_chemistry.data.unitary_representation.containers.pauli_product_formula import PauliProductFormulaContainer
from qdk_chemistry.utils.qsharp import QSHARP_UTILS

from .base import ControlledCircuitMapper

__all__: list[str] = ["ControlledPauliSequenceMapper"]


[docs] class ControlledPauliSequenceMapper(ControlledCircuitMapper): r"""Controlled evolution circuit mapper using Pauli product formula term sequences. Given a time-evolution operator expressed as a Pauli product formula :math:`U(t) \approx \left[ U_{\mathrm{step}}(t / r) \right]^{r}`, this mapper constructs a controlled version of :math:`U(t)` using the following pattern: 1. Each Pauli operator :math:`P_j` is basis-rotated into the :math:`Z` basis. 2. Qubits involved in :math:`P_j` are entangled into a sequence using CNOT gates. 3. A controlled :math:`R_z` rotation implements :math:`e^{-i\,\theta_j\,P_j} \;\rightarrow\; \text{CRZ}(2 \theta_j)`. 4. The basis rotations and entangling operations are uncomputed. Notes: * Currently supports only single-control-qubit scenarios. * Requires a ``PauliProductFormulaContainer`` for the time evolution unitary. """
[docs] def __init__(self): """Initialize the PauliSequenceMapper.""" super().__init__()
[docs] def name(self) -> str: """Return the algorithm name.""" return "pauli_sequence"
[docs] def type_name(self) -> str: """Return controlled_circuit_mapper as the algorithm type name.""" return "controlled_circuit_mapper"
def _run_impl(self, unitary: UnitaryRepresentation) -> Circuit: r"""Construct a quantum circuit implementing the controlled unitary. Args: unitary: The unitary representation containing the Hamiltonian and evolution parameters. Control and target indices are read from settings. Returns: Circuit: A quantum circuit implementing the controlled unitary :math:`U` where :math:`U` is the time evolution operator :math:`\exp(-i H t)`. Raises: ValueError: If the unitary container type is not supported. ValueError: If multiple control qubits are provided. """ unitary_container = unitary.get_container() if not isinstance(unitary_container, PauliProductFormulaContainer): raise ValueError( f"The {unitary.get_container_type()} container type is not supported. " "PauliSequenceMapper only supports PauliProductFormula container for the unitary." ) control_indices = self._get_control_indices() if len(control_indices) != 1: raise ValueError("PauliSequenceMapper currently only supports a single control qubit.") target_indices = self._get_target_indices(unitary) pauli_terms: list[list[qsharp.Pauli]] = [] angles: list[float] = [] for term in unitary_container.step_terms: base_terms = [qsharp.Pauli.I] * unitary_container.num_qubits for index, pauli in term.pauli_term.items(): base_terms[index] = getattr(qsharp.Pauli, pauli) pauli_terms.append(base_terms.copy()) angles.append(term.angle) controlled_evo_params = QSHARP_UTILS.ControlledPauliExp.RepControlledPauliExpParams( pauliExponents=pauli_terms, pauliCoefficients=angles, repetitions=unitary_container.step_reps, control=control_indices[0], systems=target_indices, ) qsharp_factory = QsharpFactoryData( program=QSHARP_UTILS.ControlledPauliExp.MakeRepControlledPauliExpCircuit, parameter=vars(controlled_evo_params), ) controlled_unitary_op = QSHARP_UTILS.ControlledPauliExp.MakeRepControlledPauliExpOp(controlled_evo_params) return Circuit(qsharp_factory=qsharp_factory, qsharp_op=controlled_unitary_op)