Evolution

class Evolution(num_modes, operator, time=1.0)

Bases: FermionicGate

Implements the time evolution of an operator.

Given a fermionic operator \(H\) and an evolution time \(t\), this gate implements the unitary

\[U = e^{-i t H}.\]

For \(U\) to be unitary, \(H\) must be Hermitian. This is the caller’s responsibility and is not verified.

Note

How this evolution is decomposed into a circuit is not fixed by this gate alone. The default _define() implementation splits the evolution group-by-group when the operator has groups assigned, and term-by-term otherwise, yielding a first-order product formula (which is exact only when the individual factors mutually commute). The transpilation process may further alter this decomposition (for example, QDriftTrotterization replaces it with a randomized product formula).

Initializing an instance of this gate can be done with the arguments listed below.

Parameters:
  • num_modes (int) – the number of fermionic modes on which this gate acts.

  • operator (OperatorTrait) – the Hermitian operator \(H\) under which to time evolve the acted-upon fermionic modes.

  • time (float) – the evolution time \(t\) entering the exponent of \(e^{-i t H}\). A negative value evolves backwards in time.

Attributes

operator

The operator under which to time evolve the acted-upon fermionic modes.

Protocol Methods

_apply_unitary_placed_(vec, norb, nelec, copy, freg_indices)

Applies exp(-i * time * operator) after relabeling the operator to global modes.

The operator is relabeled onto its global modes and turned into a scipy LinearOperator via linear_operator() (backed by a native FCI matrix-vector kernel), then applied to the vector via scipy.sparse.linalg.expm_multiply. This mirrors ffsim’s own _apply_unitary_ implementations (e.g. for its UCCSD operators).

Parameters:
  • vec (np.ndarray) – the state vector to act on.

  • norb (int) – the number of spatial orbitals of the global state vector.

  • nelec (int | tuple[int, int]) – either a single integer for a spinless system, or a pair of integers storing the numbers of spin alpha and spin beta fermions. An integer selects the spinless mode interpretation (the operator’s norb modes are alpha orbitals); a pair selects the spinful (orb, spin) interpretation of the operator’s 2 * norb modes.

  • copy (bool) – whether to copy the vector before operating on it.

  • freg_indices (list[int]) – the absolute (global) mode indices that this gate’s local modes map onto. The operator is relabeled from its local modes to these global modes before being applied, mirroring the transpiler’s synthesis pass.

Returns:

The transformed vector.

Raises:
  • NotImplementedError – if the gate’s operator is not a FermionOperator. The simulation path is currently backed by the native FCI kernel, which only FermionOperator exposes (via SupportsLinearOperator); evolving other operator types is not yet supported.

  • ValueError – if the operator does not conserve the (norb, nelec) sector. Evolving under exp(-i * time * operator) only yields a unitary when operator maps the sector to itself; a term that leaves the sector would be silently projected to zero by the native kernel, producing a non-unitary, physically meaningless result. Such an operator is therefore rejected rather than applied. For a pair nelec the alpha and beta electron counts must each be conserved (particle number and the z-component of spin), matching the fixed sector the kernel represents.

Return type:

np.ndarray