FermionicQDrift¶
- class FermionicQDrift(num_terms, *, weights=None, rng=None)¶
Bases:
objectA synthesis method that Trotterizes an
Evolutionvia the qDRIFT protocol.This method replaces the exact evolution \(e^{-i t H}\) of an
Evolutiongate by a randomized product formula: it drawsnum_termssamples from the Hamiltonian’s terms (orgroups, if assigned), with each term sampled with a probability proportional to the magnitude of its sampling weight \(w_j\), and emits oneEvolutiongate per sample. Every sampled gate evolves its (unit-magnitude, sign-preserving) term (or, when the Hamiltonian carries groups, its whole sampled group) for the same time\[\delta = \frac{\lambda t}{\texttt{num\_terms}}, \qquad p_j = \frac{w_j}{\lambda}, \qquad \lambda = \sum_j w_j,\]so that each draw contributes \(\delta \cdot p_j = w_j t / \texttt{num\_terms}\). By default \(w_j = |c_j|\), the magnitude of the sampled term’s (or group’s) coefficient, which recovers the textbook qDRIFT normalization \(\lambda = \sum_j |c_j|\); the sign of \(c_j\) is not part of the weight but of the sampled operator, and is read off it directly. Supply
weightsto precompute that array once instead of deriving it on every call.The ordered product of the sampled evolutions does not reproduce \(e^{-i t H}\) exactly; rather, its expectation over the sampling approximates the exact evolution, with an error that decreases as
num_termsgrows. Because the output depends on the random draws, it differs from run to run unless a fixedrngis supplied.The method is registered as
Evolution.QDrift, so it is on an equal footing with the product formulasFermionicLieTrotterandFermionicSuzukiTrotter:F2FSynthesis({"Evolution": ("QDrift", (100,), {"rng": 42})})
A gate can also carry the method itself, as its
synthesis, and be broken down bydecompose()(or Qiskit’sDecomposepass). This is how each gate of a circuit gets its ownnum_terms,weightsandrng:Evolution(num_modes, operator, time, synthesis=FermionicQDrift(100, weights=weights))
Note
The sampled gates are marked
Evolution.atomic: the random draw is the Trotterization this method performs, so the emitted gates are terminal factors anddecompose()leaves them in place rather than splitting them further.Note
A fixed
rngreproduces the sequence of randomizations, not an individual member of it. Successive calls draw from the same generator, so they yield different circuits; replaying from the same seed reproduces all of them in the same order, but the nth circuit cannot be obtained without drawing the preceding ones first. Generating a batch of randomizations and recording the seed therefore works as expected (see Generate SqDRIFT circuits); addressing one member directly is not supported.Hint
Terms that are diagonal in the occupation-number basis (that is, products of number operators) have no effect on the sampled bitstrings, so including them only increases the sampling overhead. Filter them out with
filter_diagonal_terms()on the Hamiltonian before constructing theEvolutiongate, rather than on every call: this method runs once per transpiled circuit, so filtering upstream avoids repeating the same filtering work for every circuit generated from the same Hamiltonian.Caution
The scale of
weightsis not free: it sets the evolution time. Since \(\delta \cdot p_j = w_j t / \texttt{num\_terms}\) above depends on \(w_j\) itself and not merely on its share of \(\lambda\), rescaling every weight by \(\gamma\) leaves the distribution untouched but evolves for \(\gamma t\) rather than \(t\). Weights are therefore absolute magnitudes, not relative preferences, and only \(w_j = |c_j|\) reproduces \(e^{-i t H}\).A distribution whose shape differs from \(|c_j|\) no longer approximates \(e^{-i t H}\) on its own either. Recovering the target evolution then requires reweighting the measured outcomes, which is the caller’s responsibility: this method emits circuits and cannot post-process their results. See
weights.See also
The qDRIFT protocol was introduced in arXiv:1811.08017. To skip the draws that cannot move a particle, see
QDriftTrotterization.Initializing an instance of this synthesis method can be done with the arguments below.
- Parameters:
num_terms (int) – the number of terms to sample for the qDRIFT Trotterization. This equals the number of
Evolutiongates emitted per input gate; a larger value reduces the Trotterization error at the cost of a deeper circuit.weights (Sequence[float] | np.ndarray | None) – the sampling weights to use instead of the coefficient magnitudes derived from the Hamiltonian. If
None(the default), they are computed from the evolved operator on every call, which reproduces the textbook qDRIFT distribution. Seeweightsfor the expected length, the sign convention and the effect on the evolution time.rng (np.random.Generator | int | None) – the random number generator (rng) to be used. When this is an
int, the internal rng will be initialized withnp.random.default_rng(seed=rng).
- Raises:
ValueError – if
weightsis not one-dimensional, is empty, holds a non-finite or negative entry, or sums to zero.
Attributes
- num_terms¶
The number of terms to include in the qDRIFT Trotterization.
- weights: ndarray | None¶
The sampling weights \(w_j\), or
Noneto derive them from the evolved operator.Supplying them hoists their computation out of the synthesis: the default path recomputes them on every call, which is repeated work when generating an ensemble from a single Hamiltonian. Deriving them once with
group_coeff_means()and passing the result here keeps this method stateless while paying that cost a single time. That is the reason to reach for this argument; passing anything other than the Hamiltonian’s own coefficient magnitudes changes which evolution the ensemble approximates (see the caution in the class docstring).One entry is expected per group when the evolved operator carries
groups, and per term otherwise; a mismatch raisesValueError. Because such an array describes one specific operator, it belongs to the gate of that operator: pass this method as thesynthesisof thatEvolutiongate, so that every gate of a circuit can hold the weights of its own operator. This method is stateless and checks only the length against each gate it is given, so it cannot tell that the array was meant for another operator of the same size. That is why giving one method by gate name toF2FSynthesissuits a circuit with a singleEvolutiongate, or one that leaves the weights unset so that each gate derives its own.The granularity follows what is sampled, which is why it is the grouping that decides it: a grouped operator is sampled group-wise, so a weight describes a whole group. Whether the grouping is the appropriate unit for a given operator is a property of that operator, not of this argument – see Group operator terms: use the operator structure, and
groups_are_hermitian()to check the most common convention.Entries must be non-negative. A weight is the magnitude \(h_j\) of the qDRIFT decomposition \(H = \sum_j h_j H_j\), in which a coefficient’s sign belongs to \(H_j\) rather than to \(h_j\) and is read off the operator directly, so a sign here would have nothing to describe.
Methods
- sampler(gate)¶
Prepares the draws for a gate without taking any of them.
- Parameters:
gate (Evolution) – the gate to sample.
- Returns:
The sampler, from which gates can be drawn in a batch or one at a time.
- Raises:
ValueError – if
weightswas supplied and its length does not match the number of groups (or terms) of the operator ofgate.- Return type:
- synthesize(gate)¶
Synthesizes the gate into
num_termssampled gates.- Parameters:
gate (Evolution) – the gate to synthesize.
- Returns:
A
FermionicCircuitholding one atomicEvolutiongate per draw.- Raises:
ValueError – if
weightswas supplied and its length does not match the number of groups (or terms) of the operator ofgate.- Return type: