Source code for qdk_chemistry.data.unitary_representation.containers.quantum_walk

"""QDK/Chemistry quantum walk operator container module."""

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

from abc import abstractmethod
from typing import Any

import h5py
import numpy as np

from qdk_chemistry.data._hashing import _hash_float, _hash_int, _hash_str

from .base import UnitaryContainer
from .block_encoding import BlockEncodingContainer

__all__: list[str] = ["LCUWalkContainer", "QuantumWalkContainer"]


[docs] class QuantumWalkContainer(UnitaryContainer): r"""Abstract base class for quantum walk operator containers. A quantum walk operator is defined as: .. math:: W = (2|0\rangle\langle 0| - I) \cdot B[H] where :math:`B[H]` is a block encoding of the Hamiltonian. The eigenvalues of :math:`W` are :math:`e^{\pm i \arccos(E_k / \lambda)}`, enabling eigenvalue extraction via quantum phase estimation. """ # Class attribute for filename validation _data_type_name = "quantum_walk_container" # Serialization version for this class _serialization_version = "0.2.0"
[docs] def eigenvalue_from_phase(self, phase_fraction: float) -> float: r"""Recover a Hamiltonian eigenvalue from a quantum-walk phase. For a walk operator whose eigenvalues are :math:`e^{\pm i \arccos(E_k / \lambda)}`, QPE measures :math:`\varphi = \arccos(E_k / \lambda) / (2\pi)`. Inverting gives: .. math:: E_k = \lambda \cos(2\pi\varphi) Args: phase_fraction: Measured phase fraction :math:`\varphi \in [0, 1)`. Returns: float: The corresponding Hamiltonian eigenvalue. """ phi = phase_fraction % 1.0 return float(self.scale * np.cos(2 * np.pi * phi))
[docs] @property @abstractmethod def power(self) -> int: """Number of times to apply the walk operator."""
[docs] class LCUWalkContainer(QuantumWalkContainer): r"""Quantum walk operator wrapping an LCU block encoding. Represents: .. math:: W^k = \left[(2|0\rangle\langle 0| - I) \cdot \text{PREPARE}^\dagger \cdot \text{SELECT} \cdot \text{PREPARE}\right]^k This container stores a reference to the underlying :class:`~qdk_chemistry.data.unitary_representation.containers.block_encoding.LCUContainer` and exposes all block-encoding data through it. """ # Class attribute for filename validation _data_type_name = "lcu_walk_container" # Serialization version for this class _serialization_version = "0.2.0"
[docs] def __init__(self, block_encoding: BlockEncodingContainer, power: int = 1, scale: float = 1.0) -> None: """Initialize an LCUWalkContainer. Args: block_encoding: The block encoding container to wrap with a reflection. power: Number of times to apply the walk operator (for :math:`W^k` in QPE). scale: The 1-norm used for eigenvalue-phase conversion. """ self._block_encoding = block_encoding self._power = power self.scale = scale super().__init__()
[docs] @property def block_encoding(self) -> BlockEncodingContainer: """Get the underlying block encoding container. Returns: The LCU block encoding that this walk operator wraps. """ return self._block_encoding
[docs] @property def power(self) -> int: """Number of times to apply the walk operator. Returns: int: The power value. """ return self._power
[docs] @property def num_qubits(self) -> int: """Total number of qubits (same as the block encoding). Returns: int: The combined qubit count. """ return self._block_encoding.num_qubits
[docs] @property def type(self) -> str: """Get the type of the unitary container. Returns: str: The type string ``"lcu_walk"``. """ return "lcu_walk"
[docs] def to_json(self) -> dict[str, Any]: """Save the LCUWalkContainer to a JSON-serializable dictionary. Returns: dict[str, Any]: Dictionary representation including container type, power, and nested block encoding. """ data: dict[str, Any] = { "container_type": self.type, "power": self.power, "scale": self.scale, "block_encoding": self._block_encoding.to_json(), } return self._add_json_version(data)
[docs] def to_hdf5(self, group: h5py.Group) -> None: """Save the LCUWalkContainer to an HDF5 group. Args: group: HDF5 group to write container data to. """ self._add_hdf5_version(group) group.attrs["container_type"] = self.type group.attrs["power"] = self.power group.attrs["scale"] = self.scale be_group = group.create_group("block_encoding") self._block_encoding.to_hdf5(be_group)
[docs] @classmethod def from_json(cls, json_data: dict[str, Any]) -> "LCUWalkContainer": """Create an LCUWalkContainer from a JSON dictionary. Args: json_data: Dictionary containing the serialized data. Returns: LCUWalkContainer: The deserialized instance. """ from .block_encoding import LCUContainer # noqa: PLC0415 cls._validate_json_version(cls._serialization_version, json_data) block_encoding = LCUContainer.from_json(json_data["block_encoding"]) return cls( block_encoding=block_encoding, power=json_data.get("power", 1), scale=json_data.get("scale", 1.0), )
[docs] @classmethod def from_hdf5(cls, group: h5py.Group) -> "LCUWalkContainer": """Load an LCUWalkContainer from an HDF5 group. Args: group: HDF5 group to read container data from. Returns: LCUWalkContainer: The deserialized instance. """ from .block_encoding import LCUContainer # noqa: PLC0415 block_encoding = LCUContainer.from_hdf5(group["block_encoding"]) power = int(group.attrs["power"]) return cls( block_encoding=block_encoding, power=power, scale=float(group.attrs.get("scale", 1.0)), )
[docs] def get_summary(self) -> str: """Get a human-readable summary of the walk operator container. Returns: str: Multi-line summary describing the walk operator and its block encoding. """ be_summary = self._block_encoding.get_summary() indented = "\n".join(" " + line for line in be_summary.splitlines()) return f"LCU Walk Operator Container:\n Power: {self.power}\n Block Encoding:\n{indented}"
def _hash_update(self, h) -> None: """Feed identifying data into the hasher.""" _hash_str(h, "lcu_walk_container") _hash_int(h, self._power) _hash_float(h, self.scale) # Delegate to block encoding's content_hash _hash_str(h, self._block_encoding.content_hash(truncate_chars=0))