Energy estimation

The EnergyEstimator algorithm in QDK/Chemistry estimates the energy of a quantum state by measuring expectation values of Pauli operators. Following QDK/Chemistry’s algorithm design principles, it takes an OpenQASM circuit (from StatePreparation) and a QubitHamiltonian (from QubitMapper) as input and returns energy expectation values with statistical uncertainty.

Overview

The EnergyEstimator evaluates the expectation value of a QubitHamiltonian with respect to a given quantum circuit that loads a wavefunction onto qubits. It takes a Circuit object with target qubit Hamiltonians and automatically generates the corresponding measurement circuits. These circuits are executed on a selected backend simulator with the user-specified number of shots, and the resulting bitstring statistics are used to calculate per-term expectation values and the total energy.

The algorithm supports:

Multiple simulator backends

QDK’s native Q# simulator or Qiskit’s Aer simulator

Noise modeling

Depolarizing noise, bit flip noise, Pauli noise, phase flip noise, and qubit loss simulation

Grouped measurements

Efficient measurement of commuting Pauli terms in a single circuit execution

Classical coefficient handling

Pre-computed classical contributions from Hamiltonian filtering

A typical workflow connects StatePreparation (which loads the wavefunction onto qubits) with EnergyEstimator (which measures the energy):

  1. Prepare a Wavefunction from a multi-configuration calculation

  2. Generate an OpenQASM circuit using StatePreparation

  3. Map the Hamiltonian to qubit operators using QubitMapper

  4. Estimate the energy using EnergyEstimator

Using the EnergyEstimator

Note

This algorithm is currently available only in the Python API.

This section demonstrates how to create, configure, and run an energy estimation. The run method returns an energy expectation value with variance and the raw measurement data.

Input requirements

The EnergyEstimator requires the following inputs:

OpenQASM circuit

A quantum circuit string in OpenQASM format that prepares the target quantum state. This is typically generated by the StatePreparation algorithm from a Wavefunction.

QubitHamiltonian

A QubitHamiltonian instance containing the Pauli-string representation of the electronic Hamiltonian. This is obtained from the QubitMapper algorithm.

Number of shots

The number of measurement repetitions to perform for statistical sampling. More shots reduce statistical uncertainty but increase computational cost.

Note

The circuit and Hamiltonian must be compatible—they should use the same qubit encoding and be derived from the same underlying molecular system. The estimator automatically generates measurement circuits for each Pauli term in the Hamiltonian and performs the circuit sampling internally.

Creating an estimator

import numpy as np
from qdk_chemistry.algorithms import create
from qdk_chemistry.data import Circuit, QubitHamiltonian

# Import noise models for Qiskit Aer simulator examples
from qiskit_aer.noise import NoiseModel, depolarizing_error

# Import noise models for QDK simulator examples
from qsharp import DepolarizingNoise

# Create energy estimator using Qsharp simulator as backend
qdk_estimator = create("energy_estimator", "qdk_base_simulator")

# Create energy estimator using Qiskit Aer simulator as backend
qiskit_estimator = create("energy_estimator", "qiskit_aer_simulator")

Configuring settings and running

Settings vary by implementation. The QDK backend supports noise models and qubit loss, while the Qiskit backend supports custom Aer noise models.

# Define a simple quantum circuit in QASM and a qubit Hamiltonian
circuit = Circuit(
    qasm="""
    include "stdgates.inc";
    qubit[2] q;
    rz(pi) q[0];
    x q[0];
    cx q[0], q[1];
    """
)
qubit_hamiltonians = [QubitHamiltonian(["ZZ"], np.array([1.0]))]

# Run energy estimation using Qsharp simulator without noise
energy_expectation_results, measurement_data = qdk_estimator.run(
    circuit, qubit_hamiltonians, total_shots=1000
)
print(
    "Energy expectation value from noiseless QDK Simulator: "
    f"{energy_expectation_results.energy_expectation_value}"
)

# Create energy estimator using Qsharp simulator with depolarizing noise
noise_model = DepolarizingNoise(0.01)
qdk_estimator = create("energy_estimator", "qdk_base_simulator")
energy_expectation_results, measurement_data = qdk_estimator.run(
    circuit, qubit_hamiltonians, total_shots=1000, noise_model=noise_model
)
print(
    "Energy expectation value from QDK Simulator with depolarizing noise: "
    f"{energy_expectation_results.energy_expectation_value}"
)

# Create energy estimator using Qsharp simulator with qubit loss
qdk_estimator = create("energy_estimator", "qdk_base_simulator", qubit_loss=0.05)
energy_expectation_results, measurement_data = qdk_estimator.run(
    circuit, qubit_hamiltonians, total_shots=1000
)
print(
    "Energy expectation value from QDK Simulator with qubit loss: "
    f"{energy_expectation_results.energy_expectation_value}"
)
# Run energy estimation using Qiskit Aer simulator without noise
energy_expectation_results, measurement_data = qiskit_estimator.run(
    circuit, qubit_hamiltonians, total_shots=1000
)
print(
    f"Energy expectation value from Qiskit Aer Simulator: {energy_expectation_results.energy_expectation_value}"
)

# Create energy estimator using Qiskit Aer simulator with noise model
noise_model = NoiseModel(basis_gates=["rz", "sx", "cx", "measure"])
noise_model.add_all_qubit_quantum_error(depolarizing_error(0.005, 1), ["rz", "sx"])
noise_model.add_all_qubit_quantum_error(depolarizing_error(0.007, 2), ["cx"])

qiskit_estimator = create("energy_estimator", "qiskit_aer_simulator")
energy_expectation_results, measurement_data = qiskit_estimator.run(
    circuit,
    qubit_hamiltonians,
    total_shots=1000,
    noise_model=noise_model,
)
print(
    "Energy expectation value from Qiskit Aer Simulator with noise: "
    f"{energy_expectation_results.energy_expectation_value}"
)

Available implementations

QDK/Chemistry’s EnergyEstimator provides a unified interface for quantum circuit simulation and energy measurement. You can discover available implementations programmatically:

from qdk_chemistry.algorithms import registry  # noqa: E402

print(registry.available("energy_estimator"))
# ['qdk_base_simulator', 'qiskit_aer_simulator']

QDK base simulator

Factory name: "qdk_base_simulator"

Native QDK/Chemistry implementation using the Q# simulator. Supports various noise models for realistic quantum hardware simulation.

Constructor parameters

Parameter

Type

Description

seed

int

Random seed for reproducibility. Default is 42.

noise_model

NoiseModel | None

Q# noise model (DepolarizingNoise, BitFlipNoise, PauliNoise, or PhaseFlipNoise). Default is None (noiseless).

qubit_loss

float

Probability of qubit loss per operation (0.0 to 1.0). Default is 0.0.

Run parameters

Parameter

Type

Description

circuit_qasm

str

OpenQASM circuit string (from StatePreparation)

qubit_hamiltonians

list[QubitHamiltonian]

List of qubit Hamiltonians to measure

total_shots

int

Total number of measurement shots

classical_coeffs

list | None

Pre-computed classical coefficients from Hamiltonian filtering. Default is None.

Qiskit Aer Simulator

Factory name: "qiskit_aer_simulator"

Energy estimation using Qiskit’s Aer simulator backend. Provides access to Qiskit’s extensive noise modeling capabilities.

Constructor parameters

Parameter

Type

Description

backend

BackendV2 | None

Qiskit Aer backend instance with optional noise model. Default is None (uses default Aer simulator).

seed

int

Random seed for reproducibility. Default is 42.

For more details on how QDK/Chemistry interfaces with external packages, see the plugin system documentation.

Further reading

  • The above examples can be downloaded as a complete Python script.

  • StatePreparation: Load wavefunctions onto qubits as quantum circuits

  • QubitMapper: Map fermionic Hamiltonians to qubit operators

  • See the examples/state_prep_energy.ipynb notebook for an end-to-end workflow demonstration

  • Settings: Configuration settings for algorithms

  • Factory Pattern: Understanding algorithm creation