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 ifreturn_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 %)