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
Symmetriesobject.
- 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 |
|---|---|---|
|
string |
Fermion-to-qubit encoding ( |
|
double |
Threshold for pruning small Pauli coefficients. Default: |
|
double |
Threshold for filtering small integrals before transformation. Default: |
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 |
|---|---|---|
|
string |
Qubit mapping strategy ( |
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 |
|---|---|---|
|
string |
Fermion-to-qubit encoding ( |
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
The above examples can be downloaded as a complete Python script.
StatePreparation: Prepare quantum circuits from wavefunctions
EnergyEstimator: Estimate energies using the qubit Hamiltonian
Settings: Configuration settings for algorithms
Factory Pattern: Understanding algorithm creation