Fermionic pools¶
Enumerate the fermionic excitation pools that ADAPT-GCiM selects from, apply their anti-Hermitian generators to state vectors without forming matrices, build exact circuits of their exponentials, and read XACC fermionic Hamiltonians into Qiskit Pauli operators. Import from nwqlib.subroutines.fermionic_pool, nwqlib.subroutines.fermionic_circuits and nwqlib.subroutines.xacc. The GCiM guide describes how the ADAPT Method uses the pools.
import numpy as np
from qiskit.quantum_info import Statevector
from nwqlib.subroutines.fermionic_circuits import build_generator_circuit
from nwqlib.subroutines.fermionic_pool import (
apply_generator_exponential,
enumerate_spin_adapted_gsd_pool,
)
pool = enumerate_spin_adapted_gsd_pool(2) # 2 spatial orbitals, 4 qubits
for generator in pool:
print(generator.family, generator.spatial_indices)
state = np.zeros(16, dtype=complex)
state[0b0011] = 1 # spatial orbital 0 doubly occupied
circuit = build_generator_circuit(pool[0], 0.3)
action = apply_generator_exponential(pool[0], state, 0.3)
print(np.allclose(Statevector(state).evolve(circuit).data, action))
single (0, 1)
double_singlet (0, 0, 0, 1)
double_singlet (0, 0, 1, 1)
double_singlet (0, 1, 1, 1)
True
The circuit of exp(0.3 A) for the first pool member agrees with its matrix-free Taylor action.
Pools and generator actions¶
Fermionic excitation pools for ADAPT-GCiM and their matrix-free actions.
Every pool member is an anti-Hermitian generator A stored as its Pauli
sum and, for the fermionic families, also as normal-ordered fermionic
terms. ADAPT-GCiM prepares exp(theta A) from it.
Conventions. Spin orbital (g, up) is mode 2*g and (g, down) is
mode 2*g + 1 for zero-based spatial orbital g, as in Zheng et al.
(2024), arXiv:2312.07691v3, Appendix E.1 (text after Eq. (E3), p. 13). Mode
j is qubit j, and the Jordan-Wigner images are those of Eq. (E8),
a_j = (X_j + i Y_j)/2 prod_{k<j} Z_k. In a Qiskit Pauli label qubit 0 is
the rightmost character, so qubit j is label position
num_qubits - 1 - j. Equation labels
E1-E8 below refer to that appendix.
Default pool (enumerate_spin_adapted_gsd_pool). The generalized
spin-adapted singles and doubles of Appendix E.1, Eqs. (E1)-(E3). Singles
come first, over spatial pairs p < q. Doubles follow over spatial pairs
(p, q) and (r, s) with p <= q, r <= s and
rank(p, q) <= rank(r, s) in lexicographic order. For each index
quadruple the triplet of Eq. (E3) precedes the singlet of Eq. (E2), and
an operator that vanishes identically is skipped. Pool indices follow this
order. The singlet and triplet couplings are those of Magoulas and
Evangelista, arXiv:2511.13485v2, Sec. III, Eqs. (6) and (7).
Orientation and sign. The builders write each generator as B - B^dagger,
normal-order it with the canonical anticommutation relations and scale it
to unit Euclidean norm over the normal-ordered coefficients. A single
(p, q) is a_{p up}^dagger a_{q up} + a_{p dn}^dagger a_{q dn} - h.c.,
Eq. (E1). The double base terms are interleaved products
a_r^dagger a_p a_s^dagger a_q with spin labels. Since
a_p a_s^dagger = delta_ps - a_s^dagger a_p, the base operator is
B = -X plus one-body terms proportional to delta_ps, where
X = a_r^dagger a_s^dagger a_p a_q (spin labels implied). The generator
is then B - B^dagger = X^dagger - X with
X^dagger = a_q^dagger a_p^dagger a_s a_r = a_p^dagger a_q^dagger a_r a_s,
which creates in (p, q) and annihilates in (r, s). So the library
double (p, q, r, s) is
Eq. (E2) or (E3) with the same index order and a positive normalization
factor. Reading only the first creation index of a base term gives the
opposite sign. The one-body terms never enter the pool, because
p == s with the ordering above forces p == q == r == s, where
B is Hermitian and the operator is skipped. This agreement holds for
every enumerated double, including repeated indices. NWQLib's tests check
all operators of Table V (Table 5 in the npj version,
doi:10.1038/s41534-024-00916-8). The q < p
condition of Eq. (E4) belongs to the elementary circuit decomposition and
does not restrict this enumeration.
Reversing an orientation replaces A by -A. At a fixed ADAPT angle
this changes the product states and the adaptive trajectory. Flipping the
sign of a molecular orbital negates every generator whose ladder operators
act on that orbital an odd number of times, which is why
nwqlib.algorithms.gcim.chemistry.build_gcim_chemistry_problem fixes the
sign of every molecular orbital.
UCCSD-SD pool (enumerate_uccsd_sd_pool). A smaller, reference-dependent
comparison pool. Occupied and virtual spin orbitals are read from a
reference occupation string, indexed by spin orbital as above. Singles
a_a^dagger a_i - h.c. run over occupied i (outer loop, ascending)
and virtual a (inner loop, ascending) with equal spin. Doubles
a_a^dagger a_b^dagger a_i a_j - h.c. run over occupied pairs i < j
(outer) and virtual pairs a < b (inner) with equal total S_z. These
are the excitation operators of Romero et al., arXiv:1701.02691v2,
Eqs. (8)-(9), in the unitary form of Eq. (17). When occupied orbitals
precede virtual ones they are also Zheng et al., arXiv:2312.07691v3,
Eqs. (E4)-(E5). Members
are normalized like the default pool. spin_orbital_indices holds
(i, a) or (i, j, a, b). The occupied-to-virtual restriction with a
fixed angle need not span the target sector, so an adaptive run on this
pool can stop above the sector minimum.
QEB-SD and OVP-CEO pools. The QEB-SD pool uses the same occupied/virtual enumeration with qubit excitations, which are Jordan-Wigner excitations without parity strings (Yordanov et al., arXiv:2011.10540v2, Eqs. (17)-(18)). The OVP-CEO pool holds sums and differences of qubit excitations on one spin-orbital set, each with a single rotation angle (Ramôa et al., arXiv:2407.08696v3, Sec. II.B.3, Eqs. (23)-(24)). ADAPT-GCiM assigns one angle to each selected operator, so the multi-parameter MVP-CEOs of that paper are not represented.
FermionicGenerator ¶
FermionicGenerator(*, family: PoolFamily, spatial_indices: tuple[int, ...], pool_index: int, num_qubits: int, fermion_terms: tuple[tuple[complex, NormalTerm], ...], pauli_terms: tuple[tuple[str, complex], ...], spin_orbital_indices: tuple[int, ...] | None = None)
One anti-Hermitian ADAPT-GCiM pool generator A, stored as its Pauli sum and, for the fermionic families, its normal-ordered terms.
The enumerate_*_pool functions return tuples of them in pool-index
order. The fields below are read-only.
Attributes:
-
family(PoolFamily) –Generator family, which fixes its excitation pattern and orientation (see the module text above):
"single","double_singlet"or"double_triplet"in the default pool,"uccsd_single"or"uccsd_double","qeb_single"or"qeb_double","ceo_ovp_single","ceo_ovp_plus"or"ceo_ovp_minus", or"custom"for a generator supplied as its own Pauli sum. -
spatial_indices(tuple[int, ...]) –Ordered spatial-orbital indices defining the generator,
(p, q)for singles and(p, q, r, s)for doubles. -
pool_index(int) –Position in the enumerated pool.
-
num_qubits(int) –Spin-orbital register width of the Pauli representation.
-
fermion_terms(tuple[tuple[complex, NormalTerm], ...]) –(coefficient, operators)pairs of the normal-ordered generator. Each operator is(mode, action)with action 1 for creation and 0 for annihilation, and the rightmost operator acts first. Empty for the qubit-excitation families. -
pauli_terms(tuple[tuple[str, complex], ...]) –(label, coefficient)pairs of the Pauli sum ofA. Pauli words are Hermitian andAis anti-Hermitian, so every coefficient is purely imaginary. -
spin_orbital_indices(tuple[int, ...] | None) –Spin orbitals of a reference-dependent member. UCCSD and QEB members and CEO singles store the occupied, then the virtual indices, ascending within each group. Opposite-spin CEO doubles store (occupied alpha, occupied beta, virtual alpha, virtual beta), and same-spin CEO doubles store their four modes sorted. None for the spin-adapted families, whose spatial indices identify them.
spin_orbital ¶
spin_orbital(spatial_orbital: int, spin: Spin) -> int
Return the spin-orbital (mode) index 2*g for spin up and 2*g + 1 for spin down of spatial orbital g.
This is the convention of Zheng et al., arXiv:2312.07691v3, Appendix E.1.
Parameters:
-
spatial_orbital(int) –Nonnegative spatial-orbital index
g. -
spin(str) –"up"or"down".
Returns:
-
mode(int) –The spin-orbital index, which is also the qubit index.
enumerate_spin_adapted_gsd_pool ¶
enumerate_spin_adapted_gsd_pool(n_spatial_orbitals: int) -> tuple[FermionicGenerator, ...]
Enumerate the generalized spin-adapted singles and doubles of Zheng et al., arXiv:2312.07691v3, Eqs. (E1)-(E3).
The order and orientation are described in the module docstring. With
P = n (n + 1) / 2 spatial pairs for n orbitals, the pool has at
most P singles and P (P + 1) doubles, because each pair of pairs
contributes at most one triplet and one singlet. Operators that vanish
identically are skipped, so the returned pool is smaller.
Parameters:
-
n_spatial_orbitals(int) –Number of spatial orbitals
n. The generators act on2 ninterleaved spin-orbital qubits.
Returns:
-
tuple[FermionicGenerator, ...]–The generators in pool-index order.
enumerate_uccsd_sd_pool ¶
enumerate_uccsd_sd_pool(reference_occupations) -> tuple[FermionicGenerator, ...]
Enumerate the occupied-to-virtual UCCSD singles and doubles of a reference.
This smaller, reference-dependent pool is a comparison baseline beside
enumerate_spin_adapted_gsd_pool.
The module text gives the ordering, index meaning and spin selection,
and the source equations (Romero et al., arXiv:1701.02691v2,
Eqs. (8)-(9) and (17)).
Parameters:
-
reference_occupations(str | Sequence[int]) –Nonempty 0/1 string or sequence whose entry
jis the occupation of spin orbitalj, in the interleaved order ofspin_orbital. It fixes the occupied and virtual spin orbitals.
Returns:
-
pool(tuple[FermionicGenerator, ...]) –The generators in pool-index order.
Raises:
-
ValueError–If
reference_occupationsis empty or contains a value other than 0 or 1.
enumerate_qeb_sd_pool ¶
enumerate_qeb_sd_pool(reference_occupations) -> tuple[FermionicGenerator, ...]
Enumerate the reference-dependent qubit-excitation singles and doubles (QEB-SD).
The occupied/virtual enumeration mirrors
enumerate_uccsd_sd_pool,
but the operators are the qubit excitations of Yordanov et al.,
arXiv:2011.10540v2, Eqs. (17)-(18), p. 6:
Q_a^dagger Q_i - Q_i^dagger Q_a and
Q_a^dagger Q_b^dagger Q_i Q_j - Q_i^dagger Q_j^dagger Q_a Q_b, with
Q_j = (X_j + i Y_j)/2 and no Jordan-Wigner parity string. Eqs. (21)-(22)
of the same paper give their exponentials as Pauli rotations.
Parameters:
-
reference_occupations(str | Sequence[int]) –Nonempty 0/1 string or sequence whose entry
jis the occupation of spin orbitalj, in the interleaved order ofspin_orbital, as forenumerate_uccsd_sd_pool.
Returns:
-
pool(tuple[FermionicGenerator, ...]) –The generators in pool-index order.
Raises:
-
ValueError–If
reference_occupationsis empty or contains a value other than 0 or 1.
enumerate_ceo_ovp_pool ¶
enumerate_ceo_ovp_pool(reference_occupations) -> tuple[FermionicGenerator, ...]
Enumerate the one-variational-parameter coupled exchange operators (OVP-CEO) of a reference.
MVP-CEOs are deliberately not represented, because they need a separate parameter for each component of one selected generator, while ADAPT-GCiM records one rotation parameter per selected operator.
The pool holds the single qubit excitations, then for each opposite-spin quadruple the plus and minus combinations of the direct and crossed double qubit excitations (Ramôa et al., arXiv:2407.08696v3, Sec. II.B.3, Eqs. (23)-(24), p. 8). For each same-spin quadruple it holds the sums and differences of every pair of its three qubit excitations, the six OVP-CEOs described on p. 9.
Parameters:
-
reference_occupations(str | Sequence[int]) –Nonempty 0/1 string or sequence whose entry
jis the occupation of spin orbitalj, in the interleaved order ofspin_orbital, as forenumerate_uccsd_sd_pool.
Returns:
-
pool(tuple[FermionicGenerator, ...]) –The generators in pool-index order.
Raises:
-
ValueError–If
reference_occupationsis empty or contains a value other than 0 or 1.
apply_generator ¶
apply_generator(generator: FermionicGenerator, state: ndarray) -> ndarray
Return A @ state for a pool generator A, from its Pauli sum, without forming a matrix.
The cost of this matrix-free action is given on the fermionic pools page.
Parameters:
-
generator(FermionicGenerator) –The generator
A. -
state(ndarray) –Complex vector of length
2**generator.num_qubits.
Returns:
-
result(ndarray) –A new vector
A @ state.
apply_generator_exponential ¶
apply_generator_exponential(generator: FermionicGenerator, state: ndarray, theta: float) -> ndarray
Return exp(theta * A) @ state for a pool generator A, by scaled Taylor steps without forming a matrix.
The state is not normalized, and a new vector of the same length is
returned. The step count is max(1, ceil(2 |theta| sum_k |c_k|)) over
the Pauli coefficients c_k of A (0 when that product is zero), so
in exact arithmetic each step h = theta / s, with s the positive
step count, has ||h A||_2 <= 1/2. Each step is a Taylor polynomial of
degree 18. Its operator 2-norm remainder is at most
eta = exp(1/2) (1/2)**19 / 19! < 2.6e-23, so its error on an input
vector v is at most eta * ||v||_2. For s steps and initial
vector v_0, the total truncation error is at most
((1 + eta)**s - 1) * ||v_0||_2 in exact arithmetic. These bounds
exclude floating-point rounding. The work is 18
matrix-free Pauli-sum actions per step. ADAPT's classical work counts
use the same step count.
Parameters:
-
generator(FermionicGenerator) –Anti-Hermitian generator
A. -
state(ndarray) –Complex vector of length
2**generator.num_qubits. -
theta(float) –Rotation angle in radians.
Returns:
-
result(ndarray) –exp(theta * A) @ stateup to the Taylor remainder above and rounding errors.
apply_spin_squared ¶
apply_spin_squared(state: ndarray, *, num_qubits: int) -> ndarray
Return S^2 @ state for the total spin, without forming a matrix, in the same Jordan-Wigner convention.
Uses S^2 = S_- S_+ + S_z (S_z + 1) with
S_+ = sum_p a_{p up}^dagger a_{p down} and S_- = S_+^dagger. The
ladder product is a two-factor matrix-free action, and S_z is
diagonal in the interleaved occupation basis.
Parameters:
-
state(array_like) –The
2**num_qubitsamplitudes in the interleaved occupation basis, qubit2pfor spatial orbitalpwith spin up and qubit2p + 1with spin down. It is converted to complex. -
num_qubits(int) –Number of spin orbitals, even.
Returns:
-
vector(ndarray) –S^2 @ state, complex, of length2**num_qubits.
Raises:
-
ValueError–If
num_qubitsis odd, if the state length is not2**num_qubits, or if the action needs more than 10 GB (decimal,10_000_000_000bytes) of known arrays or more than 1,000,000,000 scalar products. These two limits are fixed and checked before the action.
Cost of the matrix-free Pauli action¶
The Pauli action of apply_generator groups terms by their X/Y flip mask, and counting set bits with NumPy gives the Jordan–Wigner parity signs. A group sums its coefficient phases once per coordinate and applies the resulting diagonal to every supplied state column, and the output has the same coordinates as the input. For L Pauli terms, G distinct flip masks and N=2**n coordinates:
- The state is read in permuted order once per distinct flip mask, while phase formation still visits every Pauli term at every coordinate, so the arithmetic costs
O(LN + GN)for one state. - The action holds one output vector of
Ncomplex values and works in tiles of at most 1,024 coordinates. Converting the input to complex, when needed, allocates one more state-sized array. The action forms no dense operator and no table ofL * 2**nentries.
XACC Hamiltonian input¶
parse_xacc and read_xacc accept two-body XACC input (at most four ladder operators per line) and return a SparsePauliOp with its complete identity contribution. The parse_xacc entry below maps 2 a_0^dagger a_0 - 3 I to -2 I - Z, and read_xacc(path, num_modes=1) reads the same text from a local file.
Input format:
- Each line's coefficient multiplies the literal ordered operator product. Creation is
^, and annihilation has no suffix. Blank lines, whitespace, complex coefficients, scientific notation and an optional trailing+are supported. - Both entry points strip at most one leading UTF-8 byte-order mark (BOM). Two leading BOMs leave the second in the input and raise
ValueErroron line 1. - No extra one-half or one-quarter prefactors, symmetry completion or Hermitian projection are applied.
Coefficients and the cutoff:
- The default
coefficient_cutoff=0.0keeps every nonzero accumulated coefficient. Normal ordering merges equivalent terms before the Jordan–Wigner expansion. Both cross-input normal-order sums and cross-monomial Pauli sums use real and imaginarymath.fsum, to keep small residues through large cancellations. Coefficients and individual bounded products use float64, and the result is not exact real arithmetic. - Overflow or a nonfinite accumulated component raises
ValueError.fsumalso rejects intermediate overflow even if a later term could cancel it. - To approximate the mapped operator deliberately, supply an absolute
coefficient_cutoffto either entry point, for exampleparse_xacc(text, num_modes=24, coefficient_cutoff=1e-12). It removes final Pauli coefficients with magnitude at most the cutoff, after all contributions have been combined, and does not discard individual input terms or normal-order terms. - The removed coefficient magnitudes sum to an upper bound on the operator-norm change, because each Pauli has norm one. The parser does not compute or establish a bound on the resulting energy error. For an LCU, the kept label count
Lsets the SELECT index widthceil(log2(L)). Deliberate pruning can reduce this width and circuit work, and small coefficients are not automatically scientifically irrelevant.
Modes and width:
- File mode
jmaps directly to qubitj. Unlike the spin-adapted pool's interleaved convention, XACC has no implied spin ordering, and blocked and interleaved indices remain as supplied. Electron count, spin ordering and reference occupations must be supplied separately by the code that uses the operator. - The required positive
num_modesis both the output width and the caller's bound on the input, so every written mode, including those of zero-coefficient terms, must be smaller. The width is never inferred from input indices, and invalid indices are rejected before the Jordan–Wigner expansion. Valid wide models used only for resource counts are not subject to local simulation limits. - Omitting the required
num_modeskeyword raises Python's ordinaryTypeError. An invalid supplied bound or malformed XACC syntax raisesValueError. - The mapped identity includes contributions from number operators and quartic terms, so the file's scalar alone is not the qubit identity shift.
Cost:
- Parsing reads the input line by line and keeps, for each distinct normal-ordered term, the list of its coefficient contributions for stable summation, which takes
O(R)coefficient storage forRraw input terms. The packed real and imaginary parts take 16 bytes per contribution, plus array capacity and per-term overhead, and keep thefsumaccuracy. - These lists are released before mapping, which costs
O(T n)time and storage forTmerged terms onnmodes, with at most 16 Pauli strings per term. The lists of Pauli coefficient contributions hold at most16 Tcontributions during mapping and are released before Qiskit allocates the returned Pauli arrays. No dense matrix, statevector or backend is used.
XACC fermionic operators with at most four ladder operators per term, mapped as written to Jordan–Wigner Paulis.
parse_xacc ¶
parse_xacc(text: str, *, num_modes: int, coefficient_cutoff: float = 0.0) -> SparsePauliOp
Map XACC text to a qubit Hamiltonian, keeping every nonzero term by default.
Each nonblank line is (real,imag) p^ q ... +, and the trailing +
is optional. Operators multiply in their literal written order, ^
denotes creation, and a line without operators is a scalar. Decimal and
scientific notation are supported. At most four ladder operators per term
are supported, keeping normal ordering and Jordan-Wigner (JW) expansion
bounded per term.
Coefficients already include all prefactors. No symmetry completion,
Hermitian projection, spin reordering, or extra factors are applied.
Cross-input sums use real/imaginary math.fsum at both normal-order and
Pauli merging stages. Only exactly zero accumulated coefficients are
removed by default. An explicit nonnegative coefficient_cutoff removes
final Pauli coefficients of magnitude at most that absolute cutoff, after
stable merging, and it never prunes individual input or normal-order terms.
Individual coefficients and bounded products use float64 arithmetic.
The identity includes all mapped contributions, not only the file scalar.
Mode j maps to qubit j (rightmost Pauli character for mode zero).
The required positive num_modes bounds every written index before JW
expansion and supplies padding. The caller supplies it as a bound on the
input, and it is not a local-simulation limit. UTF-8 text may have an
initial byte-order mark.
The caller supplies the electron count, spin ordering and reference
occupations separately, because none of them can be inferred from this
syntax.
Parameters:
-
text(str) –XACC text.
-
num_modes(int) –Positive number of modes, which is the output width. Every written mode index must be smaller.
-
coefficient_cutoff(float, default:0.0) –Default
0.0. Nonnegative absolute cutoff on the final Pauli coefficients.
Returns:
-
hamiltonian(SparsePauliOp) –The Jordan-Wigner image on
num_modesqubits, including its complete identity term.
Raises:
-
TypeError–If
num_modesis omitted ortextis not a string. -
ValueError–With a line number, for malformed or nonfinite input, an invalid
num_modesor a mode index outside it.
Examples:
2 a_0^dagger a_0 - 3 I maps to -2 I - Z, because
a_0^dagger a_0 = (I - Z)/2:
>>> from nwqlib.subroutines.xacc import parse_xacc
>>> parse_xacc("(2,0)0^ 0 +\n(-3,0)", num_modes=1).to_list()
[('I', (-2+0j)), ('Z', (-1+0j))]
read_xacc ¶
read_xacc(path: str | PathLike[str], *, num_modes: int, coefficient_cutoff: float = 0.0) -> SparsePauliOp
Read a UTF-8 XACC file and map it to a qubit Hamiltonian, as parse_xacc does for text.
Lines are read one at a time, and the coefficient contributions of each distinct normal-ordered term are kept for stable summation. The merged polynomial is then mapped with Jordan-Wigner and stable Pauli summation. These contribution lists hold O(R) coefficients for R input terms and O(T) coefficients for T merged terms (bounded degree four), and no statevector or dense operator is constructed.
Parameters:
-
path(str | PathLike[str]) –Path of a local UTF-8 XACC file.
-
num_modes(int) –Positive number of modes, as for
parse_xacc. -
coefficient_cutoff(float, default:0.0) –Default
0.0. Nonnegative absolute cutoff on the final Pauli coefficients, as forparse_xacc.
Returns:
-
hamiltonian(SparsePauliOp) –The Jordan-Wigner image on
num_modesqubits, including its complete identity term.
Raises:
-
OSError–If the file cannot be opened.
-
TypeError–If
num_modesis omitted. -
ValueError–As for
parse_xacc, and for a file that is not valid UTF-8.
Compact generator circuits¶
build_generator_circuit(generator, theta, ...) constructs exp(theta*A) for a supported anti-Hermitian generator as a QuantumCircuit. An outer control occupies qubit zero and shifts the system modes by one. For an inverse, the builder evaluates the complete parameter map at -theta, including the even angle functions, to produce exp(-theta*A) directly. This avoids synthesizing the controls again through a generic circuit inverse.
The builder supports commuting Pauli generators and the default spin-adapted GSD index classes:
- Pair/split and four-distinct doubles use the closed-form excitation factors of Magoulas and Evangelista, arXiv:2511.13485v2. Pair/split doubles follow Sec. V, Eqs. (15), (21), (22) and (25)–(27). Four-distinct doubles follow Sec. VI, Tables I and II, with the generator mapping of Supplemental Tables SI and SII. The library angle is divided by
sqrt(2)to give the paper angle, because the paper operators have coefficient normsqrt(2)in the library's normalization. - Shared-index doubles use NWQLib's own exact block synthesis instead of those closed forms. They use at most a 64 by 64 real active-mode generator and matrix exponentials of dimension at most five.
- Fermionic routing consists of the gates that map the coupled occupations of an excitation onto one pivot qubit and attach the Jordan–Wigner signs of the occupied spectator modes. In the pair/split and four-distinct circuits these are CNOT and CZ gates, and the shared-index circuits also reorder the active modes with fermionic swaps (SWAP followed by CZ). The routing gates are undone after the rotation, so they cancel when the outer control is zero.
- Noncommuting custom generators must match a supported default family. The builder does not synthesize them through amplitudes or a generic dense unitary.
These are exact factorization formulas evaluated in floating-point arithmetic. Shared-index two-level synthesis and occupation-conditioned four-distinct rotations can require substantially more gates than small amplitude preparations. Construction is bounded in active support, and no claim of optimal gate count is made. ADAPT composes the generator circuits it chose with its initial preparation and ordered parameter values.
build_generator_circuit ¶
build_generator_circuit(generator: FermionicGenerator, theta: float, *, controlled: bool = False, inverse: bool = False, _plan: _GeneratorCircuitPlan | None = None, max_bytes: int = DEFAULT_INPUT_BYTES) -> QuantumCircuit
Build an exact circuit of exp(theta*A) for one pool generator A, optionally controlled or inverted.
Inversion evaluates the complete parameter map at -theta, including
the even angle functions such as alpha_5 of Magoulas and
Evangelista, arXiv:2511.13485v2, Eq. (27), because
exp(theta*A)^dagger = exp(-theta*A) for anti-Hermitian A. The
multi-controlled gates are therefore never passed through a generic
circuit inverse, and NWQLib's tests check this. Custom commuting Pauli
sums are supported. A noncommuting custom operator has no exact compact
decomposition here and is rejected. Construction never forms a state
vector.
For the commuting route A = sum_k i c_k P_k with real c_k, so
exp(theta*A) = prod_k exp(i theta c_k P_k).
apply_pauli_rotation(P, phi) implements exp(-i phi P / 2), hence
phi = -2 theta c_k. An identity term gives the phase
exp(i theta c_k). It is a global phase without control and a phase
gate on the control qubit under control, where the phase is physical.
Parameters:
-
generator(FermionicGenerator) –Anti-Hermitian pool generator of a supported family, or a custom commuting Pauli sum.
-
theta(float) –Finite rotation angle in radians. For a noncommuting default double-excitation generator whose creation and annihilation orbital pairs share an index, the
shared_indexconstruction groups connected occupation-basis states of the active modes into matrix blocksBof the generator. Each matrixtheta * Bmust have induced 1-normmax_j sum_i abs(theta * B[i, j]) <= 2**37, the implementation's limit before calling the matrix exponential. -
controlled(bool, default:False) –Default
False. Add one control qubit. The control is qubit 0 and system qubitjbecomes qubitj + 1. -
inverse(bool, default:False) –Default
False. Buildexp(-theta*A). -
_plan(_GeneratorCircuitPlan | None, default:None) –Default
None. Construction plan that ADAPT's planning computes once for this generator and supplies for every angle. A caller leaves it at its default. -
max_bytes(int, default:DEFAULT_INPUT_BYTES) –Default 10 GB (decimal,
10_000_000_000bytes). Byte limit for choosing the construction.
Returns:
-
circuit(QuantumCircuit) –Circuit on
generator.num_qubits + int(controlled)qubits.
Raises:
-
ValueError–If
thetais not finite ortheta * Bexceeds the limit given fortheta, if a custom generator's Pauli terms do not commute, if the generator's coefficients, indices or terms do not match a supported family, if_planwas built for another generator, or if choosing the construction exceedsmax_bytes. -
TypeError–If
generatoris not aFermionicGeneratoror one of its fields has the wrong type.