Qubit mapping

The QubitMapper algorithm in QDK/Chemistry transforms electronic-structure Hamiltonians into qubit Hamiltonians suitable for quantum computation. Following QDK/Chemistry’s algorithm design principles, it takes a Hamiltonian instance as input and produces a QubitHamiltonian instance as output. This transformation is essential for executing quantum chemistry algorithms on quantum hardware.

Overview

The QubitMapper algorithm converts fermionic Hamiltonians into qubit-operator representations composed of Pauli strings. This transformation preserves the operator algebra, particle-number constraints, and antisymmetry required by fermionic statistics. The resulting qubit Hamiltonian is mathematically equivalent to the original fermionic Hamiltonian but is now in a form that can be executed on quantum hardware or simulated by quantum algorithms.

Note

Core energy handling: The core energy (nuclear repulsion + frozen orbital contributions) from the input Hamiltonian is not included in the output QubitHamiltonian. To compute total energies, add hamiltonian.get_core_energy() to expectation values computed from the QubitHamiltonian.

Supported encodings

Different encoding strategies produce mathematically equivalent qubit Hamiltonians but with different Pauli-string structures. The choice of encoding can affect circuit depth and measurement requirements on quantum hardware. Not every implementation supports all encodings — see Available implementations for details.

Jordan-Wigner [JW28]

Encodes each fermionic mode in a single qubit whose state directly represents the orbital occupation. Fermionic antisymmetry is enforced through a Z-string on all lower-indexed qubits.

Bravyi-Kitaev [SRL12]

Distributes both occupation and parity information across qubits using a binary-tree (Fenwick tree) structure, reducing the average Pauli-string weight to O(log n).

Parity [SRL12]

Encodes qubits with cumulative electron-number parities of the orbitals.

Symmetry-conserving Bravyi-Kitaev [BGMT17]

Exploits particle-number and spin-parity symmetries to reduce the qubit count by 2. Requires a Symmetries object.

Bravyi-Kitaev tree [HavlivcekCT+17]

A tree-based variant of the Bravyi-Kitaev transformation that uses a different qubit indexing strategy.

Using the QubitMapper

Note

This algorithm is currently available only in the Python API.

This section demonstrates how to create, configure, and run a qubit mapping. The run method returns a QubitHamiltonian object containing the Pauli-string representation.

Input requirements

The QubitMapper requires the following input:

Hamiltonian

A Hamiltonian instance containing the fermionic one- and two-electron integrals. This is typically constructed using the HamiltonianConstructor algorithm.

The Hamiltonian defines the fermionic operators that will be transformed into qubit (Pauli) operators using the selected encoding strategy.

Note

Different encoding strategies produce mathematically equivalent qubit Hamiltonians but with different Pauli-string structures. The choice of encoding can affect circuit depth and measurement requirements on quantum hardware. See Supported encodings above for descriptions.

Creating a mapper

from qdk_chemistry.algorithms import create

# Create a QubitMapper instance
qubit_mapper = create("qubit_mapper", "qiskit")

Configuring settings

Settings can be modified using the settings() object. See Available implementations below for implementation-specific options.

# Configure the encoding strategy
qubit_mapper.settings().set("encoding", "jordan-wigner")

Running the calculation

from pathlib import Path

from qdk_chemistry.data import Structure

# Read a molecular structure from XYZ file
structure = Structure.from_xyz_file(Path(".") / "../data/water.structure.xyz")

# Perform an SCF calculation to generate initial orbitals
scf_solver = create("scf_solver")
_, wfn_hf = scf_solver.run(
    structure, charge=0, spin_multiplicity=1, basis_or_guess="cc-pvdz"
)

# Select an active space
active_space_selector = create(
    "active_space_selector",
    algorithm_name="qdk_valence",
    num_active_electrons=4,
    num_active_orbitals=6,
)
active_wfn = active_space_selector.run(wfn_hf)
active_orbitals = active_wfn.get_orbitals()

# Construct Hamiltonian in the active space
hamiltonian_constructor = create("hamiltonian_constructor")
hamiltonian = hamiltonian_constructor.run(active_orbitals)

# Map the fermionic Hamiltonian to a qubit Hamiltonian
qubit_hamiltonian = qubit_mapper.run(hamiltonian)
print(f"Qubit Hamiltonian has {qubit_hamiltonian.num_qubits} qubits")

Available implementations

QDK/Chemistry’s QubitMapper provides a unified interface for qubit mapping methods. You can discover available implementations programmatically:

from qdk_chemistry.algorithms import registry

print(registry.available("qubit_mapper"))
# ['qdk', 'qiskit']

QDK

Factory name: "qdk"

Native QDK/Chemistry qubit mapping implementation built on the PauliOperator expression layer. This implementation provides high-performance fermion-to-qubit transformations without external dependencies.

Supported encodings: Jordan-Wigner, Bravyi-Kitaev

The native mapper uses blocked spin-orbital ordering internally (alpha orbitals first, then beta orbitals). Use QubitHamiltonian.to_interleaved() for alternative qubit orderings if needed.

Settings

Setting

Type

Description

encoding

string

Fermion-to-qubit encoding (jordan-wigner, bravyi-kitaev). Default: jordan-wigner

threshold

double

Threshold for pruning small Pauli coefficients. Default: 1e-12

integral_threshold

double

Threshold for filtering small integrals before transformation. Default: 1e-12

Example

from qdk_chemistry.algorithms import create as create_algorithm

# Create a native QDK QubitMapper instance
qdk_mapper = create_algorithm("qubit_mapper", "qdk")

# Configure the encoding (jordan-wigner or bravyi-kitaev)
qdk_mapper.settings().set("encoding", "jordan-wigner")

# Optional: configure thresholds for numerical precision
qdk_mapper.settings().set("threshold", 1e-12)
qdk_mapper.settings().set("integral_threshold", 1e-12)

# Map the fermionic Hamiltonian to a qubit Hamiltonian
qdk_qubit_hamiltonian = qdk_mapper.run(hamiltonian)
print(f"QDK mapper produced {len(qdk_qubit_hamiltonian.pauli_strings)} Pauli terms")

Qiskit

Factory name: "qiskit"

Qubit mapping implementation integrated through the Qiskit plugin.

Supported encodings: Jordan-Wigner, Bravyi-Kitaev, Parity

Settings

Setting

Type

Description

encoding

string

Qubit mapping strategy (jordan-wigner, bravyi-kitaev, parity)

OpenFermion

Factory name: "openfermion"

Qubit mapping implementation integrated through the OpenFermion plugin.

Supported encodings: Jordan-Wigner, Bravyi-Kitaev, Symmetry-conserving Bravyi-Kitaev, Bravyi-Kitaev tree

Settings

Setting

Type

Description

encoding

string

Fermion-to-qubit encoding (jordan-wigner, bravyi-kitaev, symmetry-conserving-bravyi-kitaev, bravyi-kitaev-tree)

Note

The symmetry-conserving-bravyi-kitaev encoding requires a Symmetries object passed as the second argument to run(). This object specifies the number of active alpha and beta electrons so that the transform can exploit particle-number and spin-parity symmetries to reduce the qubit count by 2. See Symmetries for full details and factory methods.

from qdk_chemistry.data import Symmetries

# Create an OpenFermion mapper with SCBK encoding
scbk_mapper = create_algorithm(
    "qubit_mapper", "openfermion", encoding="symmetry-conserving-bravyi-kitaev"
)

# Provide symmetries: the SCBK encoding needs active electron counts
# Option 1: construct directly
symmetries = Symmetries(n_alpha=2, n_beta=2)
scbk_hamiltonian = scbk_mapper.run(hamiltonian, symmetries)

# Option 2: derive from a wavefunction
symmetries = Symmetries.from_wavefunction(active_wfn)
scbk_hamiltonian = scbk_mapper.run(hamiltonian, symmetries)

# SCBK reduces the qubit count by 2 compared to JW or BK
print(
    f"SCBK mapper: {scbk_hamiltonian.num_qubits} qubits "
    f"(vs {qdk_qubit_hamiltonian.num_qubits} for JW)"
)

Further reading