Transpiling fermionic circuits

Important

The concepts in this guide are currently available only in the Python API. Equivalent functionality will be made available via the C API in a future release.

This guide explains how to transpile a FermionicCircuit to a standard QuantumCircuit. We continue with the same time evolution example from the fermionic circuits guide.

Transpilation stages

The FermionicStagedPassManager splits transpilation into four stages:

Optimization

Fermionic-level optimizations that keep the circuit in fermionic space. These passes exploit fermionic structure and commutation relations while problem-aware knowledge remains fully available.

Layout

Maps fermionic mode registers to quantum registers. For occupation-basis mappings like Jordan-Wigner, each fermionic mode maps to a single qubit. This stage also supports more general mappings where the number of qubits differs from the number of fermionic modes.

Synthesis

Converts fermionic gates to qubit operations by using the chosen fermion-to-qubit mapping. FermionicGate instances are transformed into sequences of standard quantum gates.

Quantum

Standard Qiskit transpilation on the resulting qubit circuit, including optimization, layout, and routing for your target hardware.

A practical example: transpilation using Jordan-Wigner

Use the preset pass manager to transpile your fermionic circuit with the Jordan-Wigner fermion-to-qubit mapping:

>>> from qiskit_fermions.circuit import FermionicCircuit
>>> from qiskit_fermions.circuit.library import Evolution
>>> from qiskit_fermions.operators import FermionOperator, cre, ann
>>> from qiskit_fermions.transpiler.presets import generate_preset_jw_pass_manager
>>>
>>> # Create the same fermionic circuit from the previous guide
>>> circuit = FermionicCircuit(4)
>>> hamiltonian = FermionOperator.from_terms([
...     ([cre(0), ann(2)], 0.5),
...     ([cre(2), ann(0)], 0.5),
...     ([cre(1), ann(3)], 0.5),
...     ([cre(3), ann(1)], 0.5),
... ])
>>> hamiltonian.groups = [0, 0, 1, 1]
>>> evolution = Evolution(4, hamiltonian, time=1.0)
>>> circuit.append(evolution, circuit.register)
>>>
>>> # Generate the Jordan-Wigner transpilation pipeline
>>> pm = generate_preset_jw_pass_manager()
>>>
>>> # Transpile the fermionic circuit to a qubit circuit
>>> qubit_circuit = pm.run(circuit)
>>>
>>> # The result is a standard QuantumCircuit
>>> print(type(qubit_circuit))
<class 'qiskit.circuit.quantumcircuit.QuantumCircuit'>
// The C API for transpilation will be made available in a future release.

(png, hires.png, pdf)

The transpiled QuantumCircuit of the fermionic time evolution.

Compare with the traditional workflow

The fermionic circuits guide describes how the traditional workflow performs fermion-to-qubit encoding before building the QuantumCircuit. Compare the two approaches:

>>> from qiskit_fermions.mappers.library import jordan_wigner
>>> from qiskit.circuit import QuantumCircuit
>>> from qiskit.circuit.library import PauliEvolutionGate
>>>
>>> # Map the fermionic Hamiltonian to a qubit operator
>>> qubit_hamiltonian = jordan_wigner(hamiltonian, 4).simplify()
>>>
>>> # Build the QuantumCircuit directly
>>> quantum_circuit = QuantumCircuit(4)
>>> pauli_evolution = PauliEvolutionGate(qubit_hamiltonian, time=1.0)
>>> quantum_circuit.append(pauli_evolution, quantum_circuit.qubits)
<qiskit.circuit.instructionset.InstructionSet object at ...>

(png, hires.png, pdf)

The directly constructed QuantumCircuit of the fermionic time evolution.

Both workflows produce equivalent circuits. However, the traditional workflow requires you to implement optimization steps manually, whereas the fermionic-first approach allows you to integrate problem-aware optimizations into the transpilation process.

Advanced workflows

The preset pass manager provides a convenient starting point. For more advanced use cases, you can:

  • Customize the transpiler passes run during each stage.

  • Implement custom fermion-to-qubit mappings by creating synthesis plugins (see F2QSynthesisPlugin).

  • Build custom transpiler passes.