Hamiltonian simulation

The HamiltonianSimulation algorithm in QDK/Chemistry simulates the time evolution of a quantum system under a time-dependent Hamiltonian and measures observable expectation values. Following QDK/Chemistry’s algorithm design principles, it takes a TimeDependentQubitHamiltonian, a list of observable QubitOperator operators, and a state-preparation Circuit as input and returns a list of EnergyExpectationResult and MeasurementData pairs.

Overview

Hamiltonian simulation solves the time-dependent Schrödinger equation \(i\,\partial_t U = H(t)\,U\) on a quantum computer. For a Hamiltonian that changes with time — for example, a molecule driven by a laser pulse — the algorithm constructs a quantum circuit for the full evolution and then executes it to measure observable expectation values.

Circuit construction is delegated to an EvolutionCircuitBuilder, which handles time-stepping, propagation, and circuit-to-gates mapping. The simulation algorithm adds circuit execution and observable measurement on top.

For resource estimation (without circuit execution), use the EvolutionCircuitBuilder directly.

Using the HamiltonianSimulation

Note

This algorithm is currently available only in the Python API.

This section demonstrates how to create, configure, and run a Hamiltonian simulation. The run method returns a list of tuples containing EnergyExpectationResult and MeasurementData objects — one per observable.

Input requirements

The HamiltonianSimulation requires the following inputs:

TimeDependentQubitHamiltonian

A TimeDependentQubitHamiltonian describing how the Hamiltonian varies with time.

Observables

A list of QubitOperator operators to measure after evolution. Each observable must have the same number of qubits as the Hamiltonian.

State preparation circuit

A Circuit that prepares the initial state before time evolution. This is typically generated by the StatePreparation algorithm from a Wavefunction.

Creating a simulation algorithm

from qdk_chemistry.algorithms import create

sim = create("hamiltonian_simulation", "euler_integrator")

Configuring settings

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

from qdk_chemistry.data import AlgorithmRef

# Configure the nested evolution circuit builder
sim.settings().set(
    "evolution_circuit_builder",
    AlgorithmRef(
        "evolution_circuit_builder",
        "euler",
        total_time=1.0,
        dt=0.1,
        propagator=AlgorithmRef("propagator", "magnus", order=1),
        evolution_builder=AlgorithmRef(
            "hamiltonian_unitary_builder", "trotter", order=4, num_divisions=2
        ),
    ),
)

Running the simulation

import numpy as np
from qdk_chemistry.algorithms import create
from qdk_chemistry.algorithms.state_preparation import identity_state_prep
from qdk_chemistry.data import AlgorithmRef, DrivenQubitHamiltonian, LatticeGraph
from qdk_chemistry.utils.model_hamiltonians import create_ising_hamiltonian

# 1. Build a driven Hamiltonian: H(t) = H0 + sin(2πt)·H1
lattice = LatticeGraph.chain(4)
h0 = create_ising_hamiltonian(lattice, j=1.0, h=0.0)
h1 = create_ising_hamiltonian(lattice, j=0.0, h=0.5)
td_hamiltonian = DrivenQubitHamiltonian(h0, h1, drive=lambda t: np.sin(2 * np.pi * t))

# 2. Configure simulation
sim = create("hamiltonian_simulation", "euler_integrator")
sim.settings().set(
    "evolution_circuit_builder",
    AlgorithmRef(
        "evolution_circuit_builder",
        "euler",
        total_time=1.0,
        dt=0.1,
        propagator=AlgorithmRef("propagator", "magnus", order=1),
    ),
)

# 3. Run: evolve and measure observables
state_prep = identity_state_prep(num_qubits=td_hamiltonian.num_qubits)
observables = [h0]  # Measure ZZ energy after evolution

results = sim.run(td_hamiltonian, observables, state_prep, shots=1000)

for energy_result, measurement_data in results:
    print(f"Energy: {energy_result.energy_expectation_value:.4f}")

Available implementations

QDK/Chemistry’s HamiltonianSimulation provides a unified interface for time-dependent simulation methods. You can discover available implementations programmatically:

from qdk_chemistry.algorithms import available

print(available("hamiltonian_simulation"))

Euler integrator

Factory name: "euler_integrator"

The Euler integrator delegates circuit construction to an EulerEvolutionCircuitBuilder, then executes the resulting circuit and measures each observable independently using the configured expectation estimator.

The circuit builder handles all time-stepping, propagation, and circuit mapping. See EvolutionCircuitBuilder for details on how the evolution circuit is constructed.

Settings

Direct settings on HamiltonianSimulationSettings:

Setting

Type

Description

evolution_circuit_builder

AlgorithmRef

Evolution circuit builder used to construct the state-prep + evolution circuit. Default: AlgorithmRef to "evolution_circuit_builder" with method "euler".

circuit_executor

AlgorithmRef

Circuit executor used to run quantum circuits. Default: AlgorithmRef to "circuit_executor" with method "qdk_sparse_state_simulator".

observable_estimator

AlgorithmRef

Estimator used to compute observable expectation values. Default: AlgorithmRef to "expectation_estimator" with method "qdk".

Nested algorithm configuration (via evolution_circuit_builder):

See EvolutionCircuitBuilder for configuring:

  • total_time — Total evolution time

  • dt — Time step size

  • propagator — Propagator for computing effective Hamiltonians

  • evolution_builder — Unitary builder (e.g., Trotter)

  • circuit_mapper — Circuit compilation strategy

Further reading