qat.fermion.chemistry.qse.apply_quantum_subspace_expansion

qat.fermion.chemistry.qse.apply_quantum_subspace_expansion(hamiltonian: SpinHamiltonian, state_prep_circ: Circuit, expansion_operators: List[Observable], qpu: QPUHandler, nbshots: int = 0, threshold: float = 1e-15, return_matrices: bool = False) Tuple[float, Optional[ndarray], Optional[ndarray]]

Apply quantum subspace expansion (QSE) to the given Hamiltonian.

QSE is a method that improves the quality of noisy results at the cost of additional measurements that help write the Hamiltonian in the small subspace where it can be diagonalized classically.

If \(\langle \Psi^\star | \hat{H} | \Psi^\star \rangle\) is the VQE result, the projected Hamiltonian matrix is built from

\[H^{(s)}_{i,j} = \langle \Psi^\star | \hat{O}_i^\dagger \hat{H} \hat{O}_j | \Psi^\star \rangle\]

where \(\hat{O}_i\) is an element of expansion_operators.

Then the generalized eigenvalue problem

\[H^{(s)} \vec{x} = E S \vec{x}\]

is solved, with \(S\) the overlap matrix:

\[S_{i,j} = \langle \Psi^\star | \hat{O}_i^\dagger \hat{O}_j | \Psi^\star \rangle\]
Parameters
  • hamiltonian (SpinHamiltonian) – The Hamiltonian in its spin representation.

  • state_prep_circ (Circuit) – The state preparation circuit.

  • expansion_operators (list<SpinHamiltonian>) – The set of operators \({O_i}_i\) generating the subspace of interest.

  • qpu (QPUHandler) – The QPU.

  • nbshots (int, optional) – The number of shots. Defaults to 0: infinite number of shots.

  • threshold (float, optional) – The numerical threshold.

  • return_matrices (bool, optional) – If set to True, the function returns the matrices \(H^{(s)}\) and \(S\). Defaults to False.

Returns

Improved energy provided by the QSE procedure. matrix_h (Optional[np.ndarray]): The subspace Hamiltonian \(H^{(s)}\). Only if return_matrices is True. matrix_s (Optional[np.ndarray]): The overlap matrix \(S\). Only if return_matrices is True.

Return type

e_qse (float)

Example:

import numpy as np
from qat.core import Term
from qat.fermion import SpinHamiltonian
from qat.lang.AQASM import Program, RY, CNOT, RZ
from qat.qpus import get_default_qpu
from qat.plugins import SeqOptim

# We instantiate the Hamiltonian we want to approximate the ground state energy of
hamiltonian = SpinHamiltonian(2, [Term(1, op, [0, 1]) for op in ["XX", "YY", "ZZ"]])

# We construct the variational circuit (ansatz)
prog = Program()
reg = prog.qalloc(2)
theta = [prog.new_var(float, '\\theta_%s'%i) for i in range(3)]
RY(theta[0])(reg[0])
RY(theta[1])(reg[1])
RZ(theta[2])(reg[1])
CNOT(reg[0], reg[1])
circ = prog.to_circ()

# Construct a (variational) job with the variational circuit and the observable
job = circ.to_job(observable=hamiltonian,
                nbshots=0)

# We now build a stack that can handle variational jobs
qpu = get_default_qpu()

optimizer = SeqOptim(ncycles=10, x0=[0, 0.5, 0])
stack = optimizer | qpu

# We submit the job and print the optimized variational energy (the exact GS energy is -3)
result = stack.submit(job)
E_min = -3
print("E(VQE) = %s (err = %s %%)"%(result.value, 100*abs((result.value-E_min)/E_min)))
e_vqe = result.value

# We use the optimal parameters found by VQE
opt_circ = circ.bind_variables(eval(result.meta_data["parameter_map"]))

expansion_operators = [SpinHamiltonian(2, [], 1.0),
                    SpinHamiltonian(2, [Term(1., "ZZ", [0, 1])])]

from qat.fermion.chemistry.qse import apply_quantum_subspace_expansion
e_qse = apply_quantum_subspace_expansion(hamiltonian,
                                        opt_circ,
                                        expansion_operators,
                                        qpu,
                                        return_matrices=False)

print("E(QSE) = %s (err = %s %%)"%(e_qse, 100*abs((e_qse-E_min)/E_min)))
E(VQE) = -3.0 (err = 0.0 %)
E(QSE) = -3.0 (err = 0.0 %)