Estimators
Next, we introduce the estimator. An estimator can be understood as an abstract object that computes the expectation value of an operator function, i.e.
In our case of solving the ground state energy estimation (GSEE) problem with SPE, is the Hamiltonian and is the time-evolution operator generated by the Hamiltonian:
We provide multiple estimators for computing . We here mention 2 of them:
ExactTimeEvolutionExpectationValueEstimator
: Evaluates with matrix multiplication.TrotterTimeEvolutionHadamardTest
: A quantum circuit implementation that computes via the Hadamard test assuming is implemented with Trotterization
Set up the problem
Here, the hydrogen molecule's Hamiltonian is used as an example. To avoid redundancy, we remove the constant term from the Hamiltonian, keeping only the part with non-trivial interactions.
import numpy as np
from pyscf import gto, scf
from quri_parts.pyscf.mol import get_spin_mo_integrals_from_mole
from quri_parts.openfermion.mol import get_qubit_mapped_hamiltonian
from quri_parts.core.operator import Operator, PAULI_IDENTITY
from quri_algo.problem import QubitHamiltonianInput
mole = gto.M(atom="H 0 0 0; H 0 0 1")
mf = scf.RHF(mole).run()
hamiltonian, mapping = get_qubit_mapped_hamiltonian(
*get_spin_mo_integrals_from_mole(mole, mf.mo_coeff)
)
eff_hamiltonian = hamiltonian - Operator({PAULI_IDENTITY: hamiltonian.constant})
hamiltonian_input = QubitHamiltonianInput(mapping.n_qubits, eff_hamiltonian)
We also prepare a state for the estimation.
from quri_parts.core.state import quantum_state
state = quantum_state(mapping.n_qubits, bits=0b11)
Exact estimator
We first explain the ExactTimeEvolutionExpectationValueEstimator
. It utilizes the exact spectrum of the Hamiltonian to perform exact computation of , so we need to diagonilze the Hamiltonian to create the exact estimator.
from quri_parts.core.operator import get_sparse_matrix
from quri_algo.core.estimator.time_evolution_estimator.exact_spectrum import ExactTimeEvolutionExpectationValueEstimator
vals, vecs = np.linalg.eig(get_sparse_matrix(eff_hamiltonian).toarray())
exact_estimator = ExactTimeEvolutionExpectationValueEstimator(hamiltonian_input, vals, vecs)
We use the estimator by passing in the state and the evolution time at which we want to perform the estimation.
evolution_time = 5.0
exact_estimator(state, evolution_time)
# output:
_Estimate(value=np.complex128(-0.7327512845998837-0.6732463886941392j), error=nan)
Hadamard test estimator
Here we explain how to use the Hadamard test to perform the estimation. We provide the TimeEvolutionHadamardTest
for this purpose. To create an estimator with it, you need to first create a controlled time evolution circuit factory, which we pick to be the TrotterControlledTimeEvolutionFactory
. Also, we need to pick a sampler. Here, we pick an ideal one.
from quri_algo.core.estimator.time_evolution_estimator import TimeEvolutionHadamardTest
from quri_algo.circuit.time_evolution.trotter_time_evo import TrotterControlledTimeEvolutionCircuitFactory
from quri_vm import VM
circuit_factory = TrotterControlledTimeEvolutionCircuitFactory(hamiltonian_input, n_trotter=10)
hadamard_estimator = TimeEvolutionHadamardTest(hamiltonian_input, circuit_factory, VM().sample)
We use the above estimator by passing in a state and the time-evolution operator. In addition to these parameters, we need to specify the number of shots we want to use for the estimation. Note that the total number of shots used is 2 times the one we pass in becasue there are n_shots
are used for estimating the real part and the other n_shots
are used for estimating the imaginary part.
hadamard_estimator(state, evolution_time, n_shots=10000)
# output:
_Estimate(value=np.complex128(-0.7372-0.6656j), error=nan)
The estimator holds the 2 hadamard test circuit factories for the estimation. One for the real part estimation and the other for the imaginary part estimation.
real_test_circuit_factory = hadamard_estimator.real_circuit_factory
imag_test_circuit_factory = hadamard_estimator.imag_circuit_factory
Short cut for Hadamard test
The above is a bit cumbersome. So, we provide a convenient object TrotterTimeEvolutionHadamardTest
to replace the above steps.
from quri_algo.core.estimator.time_evolution_estimator.trotter import TrotterTimeEvolutionHadamardTest
trotter_estimator = TrotterTimeEvolutionHadamardTest(hamiltonian_input, VM().sample, n_trotter=10)
trotter_estimator(state, evolution_time, n_shots=10000)
# output:
_Estimate(value=np.complex128(-0.7284-0.6678j), error=nan)