Phase estimation

The 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 algorithm design principles, it takes a state-preparation Circuit (from StatePreparation), a QubitHamiltonian (from QubitMapper or a model Hamiltonian), a unitary builder (e.g., TimeEvolutionBuilder), a ControlledEvolutionCircuitMapper, and a CircuitExecutor as input and returns a QpeResult containing the measured phase, reconstructed energy, and alias-resolution metadata.

Overview

Quantum Phase Estimation (QPE) is one of the foundational quantum algorithms for chemistry. Given a unitary \(U\) and an initial state \(|\psi\rangle\) that has significant overlap with an eigenstate of \(U\), QPE measures the eigenphase \(\phi\) such that \(U|\psi\rangle = e^{2\pi i \phi}|\psi\rangle\). For chemistry applications the unitary is typically the Hamiltonian time-evolution operator \(U = e^{-iHt}\), in which case the energy eigenvalue is recovered as \(E = 2\pi\phi / t\). However, the algorithm itself is agnostic to how the unitary is constructed — any operator whose eigenvalues encode the quantity of interest can be used.

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

Iterative Quantum Phase Estimation (IQPE)

Kitaev’s single-ancilla algorithm [Kit95] 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 [NC10] that uses a register of \(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 QpeResult objects with automatic phase-wrapping, energy alias detection, and full serialization support. See QpeResult for details on the result data class.

Typical workflow

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

  1. Prepare a reference Wavefunction from a multi-configuration calculation

  2. Generate a state-preparation Circuit using StatePreparation

  3. Map the molecular Hamiltonian to qubit operators using QubitMapper

  4. Choose a method for constructing the target unitary (currently, QDK/Chemistry provides time-evolution builders for Hamiltonian simulation)

  5. Run phase estimation to obtain the energy eigenvalue

Alternatively, phase estimation can be used with model Hamiltonians (e.g., Hubbard, Heisenberg, or Ising spin models) or with a user-supplied Circuit and QubitHamiltonian 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 QpeResult containing the measured phase, energy, alias candidates, and measurement metadata.

Input requirements

The PhaseEstimation requires the following inputs:

State preparation circuit

A Circuit that prepares the target quantum state on the system register. This is typically generated by the StatePreparation algorithm from a Wavefunction, but can also be any user-constructed circuit (e.g., a custom initial state for a model Hamiltonian).

QubitHamiltonian

A QubitHamiltonian containing the Pauli-string representation of the Hamiltonian. This can be obtained from the QubitMapper algorithm, constructed from a model Hamiltonian, or built directly by the user.

Unitary builder

An algorithm that constructs the unitary operator \(U\) whose eigenphase is to be measured. For chemistry applications this is typically a TimeEvolutionBuilder that approximates \(U(t) = e^{-iHt}\). The builder produces a TimeEvolutionUnitary which is then converted to a controlled circuit.

ControlledEvolutionCircuitMapper

Converts a TimeEvolutionUnitary into a controlled version and synthesises it as an executable Circuit. The default implementation ("pauli_sequence") constructs controlled Pauli rotations from a PauliProductFormulaContainer. See Controlled evolution circuit mapper for details.

CircuitExecutor

A backend that executes Circuit objects and returns measurement bitstrings as CircuitExecutorData. QDK/Chemistry ships native Q# simulators (sparse-state and full-state, with optional QuantumErrorProfile noise modelling) and integrates with Qiskit’s Aer simulator through the plugin system. See Circuit execution 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.

Creating a phase estimation algorithm

from qdk_chemistry.algorithms import create

# Create the default (iterative) phase estimation algorithm
iqpe = create("phase_estimation", "iterative")

# Or create the standard QFT-based variant (requires Qiskit)
qpe = create("phase_estimation", "qiskit_standard")

Configuring settings

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

# Configure iterative phase estimation
iqpe = create("phase_estimation", "iterative")
iqpe.settings().set("num_bits", 10)
iqpe.settings().set("evolution_time", 0.1)
iqpe.settings().set("shots_per_bit", 10)
# Configure standard QFT-based phase estimation
qpe = create("phase_estimation", "qiskit_standard")
qpe.settings().set("num_bits", 10)
qpe.settings().set("evolution_time", 0.1)
qpe.settings().set("shots", 100)
qpe.settings().set("qft_do_swaps", True)

Running the calculation

Once configured, the PhaseEstimation can be executed with all required dependencies:

import numpy as np
from qdk_chemistry.algorithms import create
from qdk_chemistry.data import Structure

# 1. Setup molecule
coords = np.array([[0.0, 0.0, 0.0], [0.0, 0.0, 1.4]])
symbols = ["H", "H"]
structure = Structure(coords, symbols=symbols)

# 2. SCF
scf_solver = create("scf_solver")
E_scf, wfn_scf = scf_solver.run(
    structure, charge=0, spin_multiplicity=1, basis_or_guess="sto-3g"
)

# 3. Hamiltonian construction
hamiltonian_constructor = create("hamiltonian_constructor")
hamiltonian = hamiltonian_constructor.run(wfn_scf.get_orbitals())

# 4. Multi-configuration calculation (reference state)
cas_solver = create("multi_configuration_calculator")
E_cas, wfn_cas = cas_solver.run(hamiltonian, 1, 1)

# 5. Qubit mapping
qubit_mapper = create("qubit_mapper", encoding="jordan-wigner")
qubit_ham = qubit_mapper.run(hamiltonian)

# 6. State preparation
state_prep = create("state_prep", "sparse_isometry_gf2x")
circuit = state_prep.run(wfn_cas)

# 7. Create QPE dependencies
evolution_builder = create("time_evolution_builder", "trotter", order=2)
circuit_mapper = create("controlled_evolution_circuit_mapper", "pauli_sequence")
circuit_executor = create("circuit_executor", "qiskit_aer_simulator", seed=42)

# 8. Create and run IQPE
iqpe = create(
    "phase_estimation", "iterative", num_bits=10, evolution_time=0.1, shots_per_bit=10
)
result = iqpe.run(
    state_preparation=circuit,
    qubit_hamiltonian=qubit_ham,
    evolution_builder=evolution_builder,
    circuit_mapper=circuit_mapper,
    circuit_executor=circuit_executor,
)

# 9. Inspect results
print(result.get_summary())

Available implementations

QDK/Chemistry’s PhaseEstimation provides a unified interface for phase estimation methods. You can discover available implementations programmatically:

from qdk_chemistry.algorithms import registry

# List all registered phase estimation implementations
implementations = registry.available("phase_estimation")
print(implementations)  # e.g. ['iterative', 'qiskit_standard']

Iterative phase estimation (IQPE)

Factory name: "iterative"

Kitaev’s iterative algorithm [Kit95] 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 \(k\) (from \(0\) to \(n-1\), where \(n\) is the total number of bits):

  1. Prepare the ancilla in \(|+\rangle\)

  2. Apply the controlled evolution \(C\text{-}U^{2^{n-k-1}}\) between the ancilla and the system register

  3. Apply a phase correction \(R_z(-\Phi_k)\) based on previously measured bits

  4. Measure the ancilla to determine bit \(k\)

The phase feedback is updated using Kitaev’s recursion:

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

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

Settings

Setting

Type

Description

num_bits

int

Number of phase bits to extract. More bits yield higher energy precision.

evolution_time

float

Time parameter \(t\) in \(U = e^{-iHt}\). Determines the energy scale: \(E = 2\pi\phi / t\).

shots_per_bit

int

Number of circuit executions per bit, used for majority-vote determination. Default is 3.

Standard QFT-based phase estimation

Factory name: "qiskit_standard"

The standard QPE algorithm [NC10] uses a register of \(n\) ancilla qubits to extract all phase bits simultaneously. The circuit structure consists of:

  1. Initialize \(n\) ancilla qubits in \(|+\rangle\)

  2. For each ancilla qubit \(j \in \{0, \ldots, n-1\}\), apply the controlled evolution \(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.

Settings

Setting

Type

Description

num_bits

int

Number of ancilla qubits (phase register size). More bits yield higher energy precision.

evolution_time

float

Time parameter \(t\) in \(U = e^{-iHt}\). Determines the energy scale.

shots

int

Total measurement shots for the full circuit. Default is 3.

qft_do_swaps

bool

Whether to include the final swap layer in the inverse QFT. Default is True.

Phase aliasing and energy resolution

Because phase estimation measures a phase \(\phi \in [0, 1)\), the reconstructed energy \(E = 2\pi\phi / t\) is only unique modulo \(2\pi / t\). This means several energy values — called aliases — can produce the same measured phase.

QDK/Chemistry handles this automatically in the QpeResult data class:

  • branching contains the full list of alias energy candidates \(E + k \cdot 2\pi/t\) for \(k \in \{-2, -1, 0, 1, 2\}\) by default

  • If a reference_energy is provided (e.g., the CASCI energy), the alias closest to the reference is selected as resolved_energy

  • canonical_phase_fraction and canonical_phase_angle reflect the alias-resolved phase

See QpeResult for the full data class documentation.

Further reading