Orbital rotations¶
The orbital rotation is a fundamental operation in the simulation of fermions. This page discusses orbital rotations and their properties.
Definitions of orbital rotation¶
There are two related ways to specify an orbital rotation, by providing either a unitary matrix or an antihermitian matrix. Generally, ffsim represents orbital rotations using unitary matrices.
Unitary matrix definition¶
An orbital rotation is described by an
That is,
The mapping
for any unitary matrices
Antihermitian matrix definition¶
Instead of the unitary matrix
It is a theorem that if
Independent orbital rotations for spin-up and spin-down¶
The definitions given above assume that the same orbital rotation applies to both the spin-up and spin-down orbitals. However, it is possible for each spin sector to have an independent orbital rotation, in which case a pair of unitary matrices
Application to time evolution by a quadratic Hamiltonian¶
A quadratic Hamiltonian is an operator of the form (here we restrict the definition to Hamiltonians with particle number and spin Z symmetry)
where
Method 1: Diagonalize the quadratic Hamiltonian¶
A quadratic Hamiltonian can always be rewritten as
where the
Time evolution by
Compute the orbital energies
and the orbital rotation matrix by performing an eigendecomposition of .Perform the orbital rotation
, which corresponds to the matrix .Perform time evolution by the operator
.Perform the orbital rotation
, which corresponds to the matrix .
Method 2: Implement directly as an orbital rotation¶
The antihermitian matrix definition of an orbital rotation shows that the time evolution operator
The following code cell demonstrates the equivalence of the two methods by applying them to a randomly generated state vector and checking that the result is the same in both cases.
[1]:
import numpy as np
import scipy.linalg
import ffsim
rng = np.random.default_rng(12345)
norb = 6
nelec = (3, 3)
# Generate a random Hermitian matrix M to represent the quadratic Hamiltonian
mat = ffsim.random.random_hermitian(norb, seed=rng)
# Generate a random state vector and set the evolution time
vec = ffsim.random.random_state_vector(ffsim.dim(norb, nelec), seed=rng)
time = 1.0
# Apply time evolution via diagonalization
eigs, orbital_rotation = scipy.linalg.eigh(mat)
result1 = ffsim.apply_num_op_sum_evolution(
vec, eigs, time=time, norb=norb, nelec=nelec, orbital_rotation=orbital_rotation
)
# Apply time evolution directly as an orbital rotation
evolution_mat = scipy.linalg.expm(-1j * time * mat)
result2 = ffsim.apply_orbital_rotation(vec, evolution_mat, norb=norb, nelec=nelec)
# Check that the results match
np.testing.assert_allclose(result1, result2)
Quantum circuit implementation¶
An orbital rotation can be implemented as a quantum circuit by decomposing it into Givens rotations. Under the Jordan-Wigner transformation, the circuit can be implemented as a dense brickwork pattern of two-qubit rotations, followed by a single layer of single-qubit phase gates. The following code cell constructs and displays a Qiskit circuit for implementing an orbital rotation. Notice that the circuit consists of two independent sub-circuits acting on each spin sector.
[2]:
from qiskit.circuit import QuantumCircuit, QuantumRegister
norb = 6
nelec = (3, 3)
# Generate a random orbital rotation
orbital_rotation = ffsim.random.random_unitary(norb, seed=rng)
# Create a quantum circuit that implements this orbital rotation
qubits = QuantumRegister(2 * norb, name="q")
circuit = QuantumCircuit(qubits)
circuit.append(ffsim.qiskit.OrbitalRotationJW(norb, orbital_rotation), qubits)
# Decompose the orbital rotation into basic gates before drawing
circuit.decompose().draw("mpl", scale=0.6)
[2]:

When an orbital rotation is applied to a single electronic configuration (in the qubit picture, a computational basis state), the quantum circuit implementation can be optimized to use fewer gates, resulting in a “diamond” pattern of gates rather than a dense “brickwork” pattern. For additional information, see this paper.
The following code cell constructs and displays an example of the optimized circuit.
[3]:
# Create a quantum circuit that applies an orbital rotation to the Hartree-Fock state
circuit = QuantumCircuit(qubits)
circuit.append(ffsim.qiskit.PrepareHartreeFockJW(norb, nelec), qubits)
circuit.append(ffsim.qiskit.OrbitalRotationJW(norb, orbital_rotation), qubits)
# Optimize the orbital rotation circuit
circuit = ffsim.qiskit.PRE_INIT.run(circuit)
# Decompose the orbital rotation into basic gates before drawing
circuit.decompose().draw("mpl", scale=0.6)
[3]:
