Phase estimation
================

The :class:`~qdk_chemistry.algorithms.PhaseEstimation` algorithm in QDK/Chemistry extracts eigenvalues from a quantum state by measuring the phase accumulated under repeated application of a unitary operator.
Following QDK/Chemistry's :doc:`algorithm design principles <../design/index>`, it takes a state-preparation :class:`~qdk_chemistry.data.Circuit` (from :doc:`StatePreparation <state_preparation>`), a :class:`~qdk_chemistry.data.QubitOperator` (from :doc:`QubitMapper <qubit_mapper>` or a :doc:`model Hamiltonian <../model_hamiltonians>`), a :doc:`QpeCircuitBuilder <qpe_circuit_builder>` (which encapsulates the unitary builder and controlled circuit mapper), and a :doc:`CircuitExecutor <circuit_executor>` as input and returns a :class:`~qdk_chemistry.data.QpeResult` containing the measured phase, reconstructed energy, and alias-resolution metadata.

Overview
--------

Quantum Phase Estimation (:term:`QPE`) is one of the foundational quantum algorithms for chemistry.
Given a unitary :math:`U` and an initial state :math:`|\psi\rangle` that has significant overlap with an eigenstate of :math:`U`, :term:`QPE` measures the eigenphase :math:`\varphi` such that :math:`U|\psi\rangle = e^{2\pi i \varphi}|\psi\rangle`.

QDK/Chemistry supports two types of unitaries for QPE:

- **Time evolution** — :math:`U = e^{-iHt}` constructed via :ref:`Trotter-Suzuki decomposition <trotter-builder>`. Energy is recovered as :math:`E = -\theta / t` where :math:`\theta = 2\pi\varphi` is mapped to :math:`(-\pi, \pi]`.
- **Qubitization** — The walk operator :math:`W` constructed via :ref:`LCU block encoding <lcu-builder>`. Energy is recovered as :math:`E = \lambda \cos(\theta)` where :math:`\lambda` is the L1 norm of the Hamiltonian.

The QPE algorithm itself is agnostic to how the unitary is constructed — the choice of unitary builder determines the phase-to-energy mapping used in post-processing.

QDK/Chemistry provides two :term:`QPE` approaches, each suited to different hardware constraints:

Iterative Quantum Phase Estimation (:term:`IQPE`)
   Kitaev's single-ancilla algorithm :cite:`Kitaev1995` that extracts phase bits one at a time, from most significant to least significant, using adaptive feedback corrections between iterations.
   This approach minimizes ancilla requirements and is particularly suited to near-term hardware.

Standard QFT-based Quantum Phase Estimation
   The textbook multi-ancilla approach :cite:`Nielsen-Chuang2010-QPE` that uses a register of :math:`n` ancilla qubits and an inverse Quantum Fourier Transform to extract all phase bits simultaneously.
   This approach achieves all precision bits in a single circuit execution but requires more ancilla qubits and longer circuit depth.

Both implementations share the same interface and produce :class:`~qdk_chemistry.data.QpeResult` objects with automatic phase-wrapping, energy alias detection, and full :doc:`serialization <../data/serialization>` support.
See :doc:`../data/qpe_result` for details on the result data class.

.. _qpe-workflow:

Typical workflow
~~~~~~~~~~~~~~~~

A complete :term:`QPE` workflow connects several stages of the QDK/Chemistry pipeline.
The most common path starts from a molecular system:

1. Prepare a reference :class:`~qdk_chemistry.data.Wavefunction` from a :doc:`multi-configuration calculation <mc_calculator>`
2. Generate a state-preparation :class:`~qdk_chemistry.data.Circuit` using :doc:`StatePreparation <state_preparation>`
3. Map the molecular :class:`~qdk_chemistry.data.Hamiltonian` to qubit operators using :doc:`QubitMapper <qubit_mapper>`
4. Choose a method for constructing the target unitary — either :ref:`Trotter time-evolution <trotter-builder>` or :ref:`LCU block encoding <lcu-builder>` (qubitization)
5. Run phase estimation to obtain the energy eigenvalue

Alternatively, phase estimation can be used with :doc:`model Hamiltonians <../model_hamiltonians>` (e.g., Hubbard, Heisenberg, or Ising spin models) or with a user-supplied :class:`~qdk_chemistry.data.Circuit` and :class:`~qdk_chemistry.data.QubitOperator` directly — the full molecular-structure pipeline is not required.


Using the PhaseEstimation
-------------------------

.. note::
   This algorithm is currently available only in the Python API.

This section demonstrates how to create, configure, and run a phase estimation calculation.
The ``run`` method returns a :class:`~qdk_chemistry.data.QpeResult` containing the measured phase, energy, alias candidates, and measurement metadata.

Input requirements
~~~~~~~~~~~~~~~~~~

The :class:`~qdk_chemistry.algorithms.PhaseEstimation` requires the following inputs:

State preparation circuit
   A :class:`~qdk_chemistry.data.Circuit` that prepares the target quantum state on the system register.
   This is typically generated by the :doc:`StatePreparation <state_preparation>` algorithm from a :class:`~qdk_chemistry.data.Wavefunction`, but can also be any user-constructed circuit (e.g., a custom initial state for a :doc:`model Hamiltonian <../model_hamiltonians>`).

QubitOperator
   A :class:`~qdk_chemistry.data.QubitOperator` containing the Pauli-string representation of the Hamiltonian.
   This can be obtained from the :doc:`QubitMapper <qubit_mapper>` algorithm, constructed from a :doc:`model Hamiltonian <../model_hamiltonians>`, or built directly by the user.

Settings
   The :class:`~qdk_chemistry.algorithms.PhaseEstimation` is configured via its settings object, which includes:

   - ``qpe_circuit_builder`` — A :class:`~qdk_chemistry.data.AlgorithmRef` to a :doc:`QpeCircuitBuilder <qpe_circuit_builder>` that handles circuit composition.
     The circuit builder encapsulates a :doc:`HamiltonianUnitaryBuilder <hamiltonian_unitary_builder>` and a :class:`~qdk_chemistry.algorithms.ControlledCircuitMapper` as its own nested algorithms.
     See :doc:`qpe_circuit_builder` for detailed configuration.

   - ``circuit_executor`` — A :class:`~qdk_chemistry.data.AlgorithmRef` to a backend that executes :class:`~qdk_chemistry.data.Circuit` objects and returns measurement bitstrings as :class:`~qdk_chemistry.data.CircuitExecutorData`.
     QDK/Chemistry ships native Q# simulators (sparse-state and full-state, with optional :class:`~qdk_chemistry.data.QuantumErrorProfile` noise modelling) and integrates with Qiskit's Aer simulator through the :doc:`plugin system <../plugins>`.
     See :doc:`circuit_executor` for details.

.. note::

   The state preparation circuit and qubit Hamiltonian must be compatible — they should use the same qubit encoding and be derived from the same underlying system.

   The ``qpe_circuit_builder`` is configured with nested algorithm references for the unitary builder and controlled circuit mapper, as shown in the configuration examples below.

.. rubric:: Creating a phase estimation algorithm

.. tab:: Python API

   .. literalinclude:: ../../../_static/examples/python/phase_estimation.py
      :language: python
      :start-after: # start-cell-create
      :end-before: # end-cell-create

.. rubric:: Configuring settings

Settings vary by implementation.
See `Available implementations`_ below for implementation-specific options.

.. tab:: Python API (Iterative)

   .. literalinclude:: ../../../_static/examples/python/phase_estimation.py
      :language: python
      :start-after: # start-cell-configure-iqpe
      :end-before: # end-cell-configure-iqpe

.. tab:: Python API (Standard)

   .. literalinclude:: ../../../_static/examples/python/phase_estimation.py
      :language: python
      :start-after: # start-cell-configure-standard
      :end-before: # end-cell-configure-standard

.. rubric:: Running the calculation

Once configured, the :class:`~qdk_chemistry.algorithms.PhaseEstimation` can be executed with all required dependencies:

.. tab:: Python API

   .. literalinclude:: ../../../_static/examples/python/phase_estimation.py
      :language: python
      :start-after: # start-cell-run
      :end-before: # end-cell-run

Available implementations
-------------------------

QDK/Chemistry's :class:`~qdk_chemistry.algorithms.PhaseEstimation` provides a unified interface for phase estimation methods.
You can discover available implementations programmatically:

.. tab:: Python API

   .. literalinclude:: ../../../_static/examples/python/phase_estimation.py
      :language: python
      :start-after: # start-cell-list-implementations
      :end-before: # end-cell-list-implementations

.. _iqpe-algorithm:

Iterative phase estimation (IQPE)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. rubric:: Factory name: ``"qdk_iterative"``

Kitaev's iterative algorithm :cite:`Kitaev1995` uses a single ancilla qubit to extract phase bits sequentially, from the most significant bit (MSB) to the least significant bit (LSB).
At each iteration :math:`k` (from :math:`0` to :math:`n-1`, where :math:`n` is the total number of bits):

1. Prepare the ancilla in :math:`|+\rangle`
2. Apply the controlled evolution :math:`C\text{-}U^{2^{n-k-1}}` between the ancilla and the system register
3. Apply a phase correction :math:`R_z(-\Phi_k)` based on previously measured bits
4. Measure the ancilla to determine bit :math:`k`

The phase feedback is updated using Kitaev's recursion:

.. math::

   \Phi_{k} = \frac{\Phi_{k+1}}{2} + \frac{\pi \cdot b_k}{2}

where :math:`b_k` is the measured bit.
Each bit is determined by a majority vote over multiple circuit executions (controlled by ``shots_per_bit``).

.. rubric:: Settings

Direct settings on :class:`~qdk_chemistry.algorithms.phase_estimation.iterative_phase_estimation.IterativePhaseEstimation`:

.. list-table::
   :header-rows: 1
   :widths: 25 15 60

   * - Setting
     - Type
     - Description
   * - ``shots_per_bit``
     - int
     - Number of circuit executions per bit, used for majority-vote determination. Default is 3.

Nested algorithm configuration (via ``qpe_circuit_builder``):

See :doc:`qpe_circuit_builder` for configuring:

- ``num_bits`` — Number of phase bits to extract
- ``unitary_builder`` → ``time`` — Time parameter :math:`t` in :math:`U = e^{-iHt}` (Trotter)
- ``unitary_builder`` → ``quantum_walk`` — Enable walk operator for qubitization (LCU)
- ``controlled_circuit_mapper`` — Circuit synthesis strategy


.. _standard-qpe-algorithm:

Standard QFT-based phase estimation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. rubric:: Factory name: ``"qdk_standard"``

The standard :term:`QPE` algorithm :cite:`Nielsen-Chuang2010-QPE` uses a register of :math:`n` ancilla qubits to extract all phase bits simultaneously.
The circuit structure consists of:

1. Initialize :math:`n` ancilla qubits in :math:`|+\rangle`
2. For each ancilla qubit :math:`j \in \{0, \ldots, n-1\}`, apply the controlled evolution :math:`C\text{-}U^{2^j}`
3. Apply an inverse Quantum Fourier Transform (iQFT) to the ancilla register
4. Measure all ancilla qubits

The phase is extracted from the dominant bitstring in the measurement results.

.. rubric:: Settings

Direct settings on :class:`~qdk_chemistry.algorithms.phase_estimation.standard_phase_estimation.StandardPhaseEstimation`:

.. list-table::
   :header-rows: 1
   :widths: 25 15 60

   * - Setting
     - Type
     - Description
   * - ``shots``
     - int
     - Total measurement shots for the full circuit. Default is 3.

Nested algorithm configuration (via ``qpe_circuit_builder``):

See :doc:`qpe_circuit_builder` for configuring:

- ``num_bits`` — Number of ancilla qubits (phase register size)
- ``unitary_builder`` → ``time`` — Time parameter :math:`t` in :math:`U = e^{-iHt}` (Trotter)
- ``unitary_builder`` → ``quantum_walk`` — Enable walk operator for qubitization (LCU)
- ``qft_do_swaps`` — Whether to include swap gates in the inverse QFT (Qiskit only)
- ``controlled_circuit_mapper`` — Circuit synthesis strategy


Phase-to-energy extraction
---------------------------

The method for converting a measured phase :math:`\varphi \in [0, 1)` to energy depends on the type of unitary used.

Time evolution (Trotter)
~~~~~~~~~~~~~~~~~~~~~~~~

When QPE acts on :math:`U = e^{-iHt}`, the phase angle :math:`\theta = 2\pi\varphi` is mapped to :math:`(-\pi, \pi]` and energy is:

.. math::

   E = \frac{\theta}{t}

Aliasing can be resolved by enumerating candidates :math:`E_{\mathrm{raw}} + k \cdot 2\pi/t` and selecting the one closest to a reference energy (e.g., :term:`CASCI`).

.. tab:: Python API (Time evolution configuration)

   .. literalinclude:: ../../../_static/examples/python/phase_estimation.py
      :language: python
      :start-after: # start-cell-configure-iqpe
      :end-before: # end-cell-configure-iqpe

Qubitization (LCU block encoding)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

When QPE acts on the qubitization walk operator :math:`W`, the eigenvalues are :math:`e^{\pm i \arccos(E/\lambda)}` and energy is recovered as:

.. math::

   E = \lambda \cos(\theta)

where :math:`\lambda = \sum_j |\alpha_j|` is the L1 norm of the Hamiltonian coefficients.

To use qubitization QPE, the :ref:`LCU builder <lcu-builder>` must be configured with ``quantum_walk=True``.
This wraps the raw block encoding in a quantum walk operator :math:`W = (2|0\rangle\langle 0| - I) \cdot B[H]`, which is required for the :math:`\arccos` eigenvalue-phase relationship.

.. tab:: Python API (Qubitization configuration)

   .. literalinclude:: ../../../_static/examples/python/phase_estimation.py
      :language: python
      :start-after: # start-cell-configure-qubitization
      :end-before: # end-cell-configure-qubitization

See :doc:`../data/qpe_result` for the full data class documentation.


Related classes
---------------

- :class:`~qdk_chemistry.data.QpeResult`: Output data class containing phase, energy, and alias information
- :class:`~qdk_chemistry.data.QubitOperator`: Input qubit Hamiltonian
- :class:`~qdk_chemistry.data.Circuit`: State-preparation circuit from :doc:`StatePreparation <state_preparation>`
- :class:`~qdk_chemistry.data.UnitaryRepresentation`: Output of :doc:`HamiltonianUnitaryBuilder <hamiltonian_unitary_builder>`
- :class:`~qdk_chemistry.data.CircuitExecutorData`: Measurement results from :doc:`CircuitExecutor <circuit_executor>`
- :class:`~qdk_chemistry.data.QuantumErrorProfile`: Noise model for noisy simulation

Further reading
---------------

- The above examples can be downloaded as a complete `Python <../../../_static/examples/python/phase_estimation.py>`_ script.
- :doc:`QpeCircuitBuilder <qpe_circuit_builder>`: Abstract base class for phase estimation circuit builders
- :doc:`HamiltonianUnitaryBuilder <hamiltonian_unitary_builder>`: Hamiltonian simulation via Trotter-Suzuki decomposition or block-encoding methods
- :doc:`CircuitExecutor <circuit_executor>`: Quantum circuit execution backends
- :doc:`StatePreparation <state_preparation>`: Load wavefunctions onto qubits as quantum circuits
- :doc:`QubitMapper <qubit_mapper>`: Map fermionic Hamiltonians to qubit operators
- :doc:`QpeResult <../data/qpe_result>`: Phase estimation result data class
- See the ``examples/qpe_stretched_n2.ipynb`` notebook for a complete end-to-end :term:`QPE` workflow
- :doc:`Settings <settings>`: Configuration settings for algorithms
- :doc:`Factory Pattern <factory_pattern>`: Understanding algorithm creation
