"""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)