"""QDK/Chemistry PREPARE-SELECT-PREPARE 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_chemistry.algorithms.circuit_mapper.psp_mapper import PSPMapper
from qdk_chemistry.data import AlgorithmRef
from qdk_chemistry.data.circuit import Circuit, QsharpFactoryData
from qdk_chemistry.data.unitary_representation.base import UnitaryRepresentation
from qdk_chemistry.utils.qsharp import QSHARP_UTILS
from .base import ControlledCircuitMapper, ControlledCircuitMapperSettings
__all__: list[str] = ["ControlledPSPMapper", "ControlledPSPMapperSettings"]
[docs]
class ControlledPSPMapperSettings(ControlledCircuitMapperSettings):
"""Settings for the ControlledPSPMapper.
Attributes:
prepare: Algorithm reference for the PREPARE oracle state preparation.
Defaults to ``DensePureStatePreparation``.
"""
[docs]
def __init__(self):
"""Initialize the settings for ControlledPSPMapper."""
super().__init__()
self._set_default(
"prepare",
"algorithm_ref",
AlgorithmRef("state_prep", "dense_pure_state"),
"Algorithm for the PREPARE oracle state preparation. ",
)
[docs]
class ControlledPSPMapper(ControlledCircuitMapper):
r"""Controlled circuit mapper using the PREPARE-SELECT-PREPARE pattern.
A wrapper over :class:`~qdk_chemistry.algorithms.circuit_mapper.psp_mapper.PSPMapper`, which
owns the PREPARE and SELECT oracles:
.. math::
B[H] = \mathrm{PREPARE}^\dagger \cdot \mathrm{SELECT} \cdot \mathrm{PREPARE}
When the input is an :class:`~qdk_chemistry.data.unitary_representation.containers.quantum_walk.LCUWalkContainer`,
the block encoding is additionally wrapped with the reflection operator to form a quantum walk:
.. math::
W = (2|0\rangle\langle 0| - I) \cdot B[H]
"""
[docs]
def __init__(self):
"""Initialize the ControlledPSPMapper."""
super().__init__()
self._settings = ControlledPSPMapperSettings()
[docs]
def name(self) -> str:
"""Return the algorithm name.
Returns:
str: The name ``"prepare_select_prepare"``.
"""
return "prepare_select_prepare"
[docs]
def type_name(self) -> str:
"""Return the algorithm type name.
Returns:
str: The type name ``"controlled_circuit_mapper"``.
"""
return "controlled_circuit_mapper"
def _block_mapper(self) -> PSPMapper:
"""Build the uncontrolled PSP mapper to delegate the block encoding to.
Returns:
A :class:`PSPMapper` carrying this mapper's ``prepare`` setting.
"""
mapper = PSPMapper()
mapper.settings().set("prepare", self._settings.get("prepare"))
return mapper
def _run_impl(self, unitary: UnitaryRepresentation) -> Circuit:
r"""Construct a controlled block-encoding circuit.
Args:
unitary: The unitary representation containing either an
:class:`LCUContainer` (plain block encoding) or an
:class:`LCUWalkContainer` (quantum walk).
Returns:
Circuit: A quantum circuit implementing the controlled block encoding.
Raises:
ValueError: If the control qubit is not a single qubit at index 0.
"""
control_indices = self._get_control_indices()
if len(control_indices) != 1 or control_indices[0] != 0:
raise ValueError("ControlledPSPMapper currently only supports a single control qubit at index 0.")
block_mapper = self._block_mapper()
container = unitary.get_container()
lcu, use_quantum_walk = block_mapper.resolve_lcu(container)
prepare_op, select_op, num_system = block_mapper.build_prepare_select_ops(container)
step_op = QSHARP_UTILS.PrepSelPrep.MakePrepSelPrepOp(prepare_op, select_op, num_system)
if use_quantum_walk:
reflection_op = QSHARP_UTILS.PrepSelPrep.MakeAncillaReflectionOp(num_system)
step_op = QSHARP_UTILS.PrepSelPrep.MakeWalkOp(step_op, reflection_op)
controlled_op = QSHARP_UTILS.CircuitComposition.MakeControlledOp(step_op)
repeated_op = QSHARP_UTILS.CircuitComposition.MakeRepeatedOp(
"ControlledPSPWalk" if use_quantum_walk else "ControlledPrepSelPrep",
controlled_op,
container.power,
)
qsharp_factory = QsharpFactoryData(
program=QSHARP_UTILS.PrepSelPrep.MakeControlledPrepSelPrepCircuit,
parameter={
"prepareOp": prepare_op,
"selectOp": select_op,
"numSystemQubits": num_system,
"numAncillaQubits": lcu.num_prepare_ancillas,
"power": container.power,
"useWalk": use_quantum_walk,
},
)
return Circuit(
qsharp_factory=qsharp_factory,
qsharp_op=QSHARP_UTILS.CircuitComposition.MakeSingleControlOp(repeated_op),
)