bowtie_qgt.bowtieqgt

Bowtie Quantum Geometric Tensor (QGT) computation module.

This module computes the Quantum Geometric Tensor (QGT), McLachlan gradients and the energy variance of a parameterized quantum circuit with the “bowtie” method. Each parameter and each Hamiltonian term gets an auxiliary circuit (a bowtie) restricted to its light cone, so the cost is exponential in the light-cone width rather than in the total number of qubits.

All quantities come out of a single complex matrix, the generalized QGT: the connected QGT of the circuit extended by one first-order Trotter step of the Hamiltonian. See BowtieQGT for the conventions.

Example

>>> bowtie = BowtieQGT(qc, hamiltonian)
>>> generalized_qgt, energy = bowtie.evaluate(parameter_values)
>>> qgt = bowtie.extract_qgt(generalized_qgt)
>>> b = bowtie.extract_gradient(generalized_qgt, which="real_time")
>>> theta_dot = np.linalg.solve(qgt + 1e-6 * np.eye(len(b)), b)

Main Class

class bowtie_qgt.bowtieqgt.BowtieQGT(qc, hamiltonian, simulator='CPU', verbosity=0)[source]

Quantum Geometric Tensor, gradients and variance via light-cone bowties.

Conventions:

Let \(|\psi(\theta)\rangle = U(\theta)|0\rangle\) and \(H = \sum_j c_j h_j\) with Pauli terms \(h_j\). Extend the parameter tangents \(|\partial_i\psi\rangle\) with one tangent per Hamiltonian term, \(-i h_j|\psi\rangle\) (the derivative of \(e^{-i c_j h_j t}\) with respect to \(c_j t\), i.e. a first-order Trotter step appended to the circuit). The generalized QGT is the connected QGT of all these tangents \(\tau_a\):

\[\tilde G_{ab} = \langle\tau_a|\tau_b\rangle - \langle\tau_a|\psi\rangle\langle\psi|\tau_b\rangle,\]

a complex Hermitian matrix of shape (N + M, N + M) with N circuit parameters and M non-identity Hamiltonian terms, ordered like qc.parameters and then the terms of hamiltonian. Its blocks give:

  • QGT: \(G = \tilde G_{:N,:N}\). Its real part is the Fubini-Study metric (the matrix of McLachlan’s principle), its imaginary part the Berry curvature. Rotations follow Qiskit’s convention \(e^{-i\theta P/2}\), so this matches qiskit_algorithms.gradients.ReverseQGT.

  • McLachlan gradients \(\tilde G_{:N,N:}\,c\): its real part is the real-time vector \(b_i = \mathrm{Im}(\langle\partial_i\psi|H|\psi\rangle - \langle\partial_i\psi|\psi\rangle E)\), its imaginary part the imaginary-time vector \(b_i = -\mathrm{Re}(\langle\partial_i\psi|H|\psi\rangle - \langle\partial_i\psi|\psi\rangle E)\), which equals \(-\tfrac12\,\partial_i E\) for Hermitian \(H\).

  • Variance: \(\mathrm{Var}[H] = c^\dagger \tilde G_{N:,N:}\, c\).

McLachlan’s principle then reads \(\mathrm{Re}(G)\,\dot\theta = b\). Identity terms of the Hamiltonian have no tangent; they only shift the energy.

qc

The parameterized quantum circuit.

hamiltonian

The Hamiltonian passed at construction.

coeffs

Coefficients of the non-identity Hamiltonian terms, in bowtie order.

energy_offset

Sum of the coefficients of identity terms.

simulator

Aer simulator that runs the bowtie circuits.

verbosity

Progress reporting level, see __init__().

bowties

Bowtie circuits, parameters first and then Hamiltonian terms.

active_qubits

Qubits each bowtie acts on.

non_zero_indices

(i, j) pairs, i >= j, whose bowties share qubits. Every other entry of the generalized QGT is exactly zero.

slices

Precomputed tensor slices for each pair in non_zero_indices.

last_timings

Seconds spent in the last evaluate() call, with keys "simulation" and "assembly".

__init__(qc, hamiltonian, simulator='CPU', verbosity=0)[source]

Build and transpile the bowtie circuits.

Parameters:
  • qc (QuantumCircuit) – Parameterized circuit. Every parameter must be the bare angle of exactly one rx, ry, rz, rxx, ryy or rzz gate.

  • hamiltonian (SparsePauliOp) – Hamiltonian as a SparsePauliOp. Identity terms are allowed and only shift the energy.

  • simulator (str | AerSimulator) – "CPU" (default) or "GPU" for a default statevector AerSimulator on that device. For full control (precision, threads, …) pass a noiseless statevector AerSimulator instead, e.g. AerSimulator(method="statevector", max_parallel_threads=8).

  • verbosity (int) – 0 is silent. 1 shows progress bars while building the bowties and prints the distribution of light-cone widths. 2 also reports progress and timings on every evaluate() call.

Raises:

ValueError – If the circuit is not supported (see validate_circuit()), the device is not "CPU" or "GPU", or the simulator is not a noiseless statevector simulator.

Warns:

UserWarning – If the Hamiltonian is a multiple of the identity.

evaluate(parameter_values)[source]

Compute the generalized QGT and the energy at the given parameters.

Parameters:

parameter_values (Mapping[Parameter, float] | Sequence[float]) – Values for every circuit parameter, either as a sequence ordered like qc.parameters or as any mapping that QuantumCircuit.assign_parameters accepts (Parameter, ParameterVector or parameter-name keys).

Return type:

tuple[ndarray, float | complex]

Returns:

(generalized_qgt, energy). generalized_qgt is the complex (N + M, N + M) matrix described in BowtieQGT; pass it to the extract_* methods. energy is \(\langle\psi|H|\psi\rangle\), a float when the Hamiltonian is Hermitian (real coefficients).

Raises:

ValueError – If a circuit parameter has no value.

extract_qgt(generalized_qgt, part='real')[source]

Extract the (N, N) QGT block.

Parameters:
  • generalized_qgt (ndarray) – Matrix returned by evaluate().

  • part (str) – "real" (default) for the Fubini-Study metric, which is what McLachlan’s variational principle uses for both real and imaginary time. "imag" for the Berry curvature, "complex" for the full QGT, as used by other variational principles.

Return type:

ndarray

Returns:

The requested part of the QGT.

extract_gradient(generalized_qgt, which)[source]

Extract McLachlan’s right-hand side b, so that Re(G) θ̇ = b.

Parameters:
  • generalized_qgt (ndarray) – Matrix returned by evaluate().

  • which (str) – "real_time" for \(e^{-iHt}\) (VarQRTE) or "imag_time" for \(e^{-H\tau}\) (VarQITE). For Hermitian H the imaginary-time vector is \(-\tfrac12\nabla_\theta E\).

Return type:

ndarray

Returns:

Real array of shape (N,).

extract_variance(generalized_qgt)[source]

Extract the energy variance \(\langle H^2\rangle - \langle H\rangle^2\).

Parameters:

generalized_qgt (ndarray) – Matrix returned by evaluate().

Return type:

float

Returns:

The variance. It vanishes exactly on eigenstates of H.

Overlap Helpers

Low-level functions used by BowtieQGT to evaluate overlaps of bowtie states on their shared qubits.

bowtie_qgt.bowtieqgt.connected_overlap(svAB, sliceab, svBC, slicebc)[source]

Compute the connected overlap ⟨ψ_AB|ψ_BC⟩ - ⟨ψ_AB|0⟩⟨0|ψ_BC⟩.

Bowtie states are expressed in the frame where the variational state is |0...0⟩, so subtracting the |0...0⟩ components projects out the global-phase direction. This makes every block of the generalized QGT gauge invariant.

Parameters:
  • svAB – First statevector tensor.

  • sliceab – Slice indices for svAB.

  • svBC – Second statevector tensor.

  • slicebc – Slice indices for svBC.

Returns:

Complex number representing the connected overlap.

bowtie_qgt.bowtieqgt.sparse_overlap_tensors(svAB, sliceab, svBC, slicebc)[source]

Compute the overlap ⟨ψ_AB|ψ_BC⟩ between statevectors with different support.

Calculates the inner product between two statevectors by projecting them onto their common support region (region B). The Hilbert space is decomposed as:

  • Region A: qubits where only ψ_AB has support → projected to |0⟩ in ψ_AB

  • Region B: qubits where both have support → full overlap computed

  • Region C: qubits where only ψ_BC has support → projected to |0⟩ in ψ_BC

The slices sliceab and slicebc project each statevector onto region B, setting amplitudes in regions A and C to their |0⟩ components.

Parameters:
  • svAB (ndarray) – Statevector tensor ψ_AB reshaped to [2, 2, …, 2] with dimensions corresponding to its active qubits.

  • sliceab – Slice for projecting ψ_AB onto region B.

  • svBC (ndarray) – Statevector tensor ψ_BC reshaped to [2, 2, …, 2] with dimensions corresponding to its active qubits.

  • slicebc – Slice for projecting ψ_BC onto region B.

Returns:

Complex number representing the overlap ⟨ψ_AB|ψ_BC⟩ computed over the common support region B.

Note

This is the core operation for computing QGT matrix entries. The sparse tensor approach avoids explicitly constructing full statevectors on the entire Hilbert space, significantly reducing memory and computation.

bowtie_qgt.bowtieqgt.get_slice(AB, BC)[source]

Generate slice indices for projecting ψ_AB onto the overlap region with ψ_BC.

Computes the slice to apply to statevector ψ_AB when computing its overlap with ψ_BC. The Hilbert space is decomposed into three regions:

  • Region A: qubits where only ψ_AB has support (projected to |0⟩)

  • Region B: qubits where both ψ_AB and ψ_BC have support (overlap computed)

  • Region C: qubits where only ψ_BC has support (projected to |0⟩)

For qubits in region B (intersection of AB and BC), the slice uses slice(None) to keep all amplitudes. For qubits in region A (in AB but not in BC), the slice uses index 0 to project onto |0⟩.

Parameters:
  • AB (tuple[int]) – Tuple of active qubit indices for statevector ψ_AB.

  • BC (tuple[int]) – Tuple of active qubit indices for statevector ψ_BC.

Returns:

List of slice objects or integers for indexing the ψ_AB tensor, in reversed order to match tensor dimension ordering:

  • slice(None) for qubits in the overlap region B (in both AB and BC)

  • 0 for qubits in region A (in AB but not in BC)

Note

When precomputing slices for overlap ⟨ψ_AB|ψ_BC⟩, both directions must be computed and stored: one slice for ψ_AB and one for ψ_BC, since the overlap is not symmetric when the active qubit sets differ.

Example

>>> # ψ_AB has support on qubits (0, 1, 2), ψ_BC on qubits (1, 2, 3,4)
>>> # Region A = {0}, Region B = {1, 2}, Region C = {3,4}
>>> get_slice((0, 1, 2), (1, 2, 3, 4))
[slice(None, None, None), slice(None, None, None), 0]
>>> # Qubit 0 (region A) → 0, qubits 1,2 (region B) → slice(None)
bowtie_qgt.bowtieqgt.zeroth(tensor)[source]

Returns tensor[0,0,0,0,…] of a given ndarray