Skip to main content

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.

f(O).\begin{equation} \langle f(\mathcal{O})\rangle. \end{equation}

In our case of solving the ground state energy estimation (GSEE) problem with SPE, O\mathcal{O} is the Hamiltonian HH and ff is the time-evolution operator generated by the Hamiltonian:

f(O)=eiHt.\begin{equation} \langle f(\mathcal{O})\rangle = \langle e^{-iHt} \rangle . \end{equation}

We provide multiple estimators for computing eiHt\langle e^{-iHt} \rangle. We here mention 2 of them:

  • ExactTimeEvolutionExpectationValueEstimator: Evaluates eiHt\langle e^{-iHt} \rangle with matrix multiplication.
  • TrotterTimeEvolutionHadamardTest: A quantum circuit implementation that computes eiHt\langle e^{-iHt} \rangle via the Hadamard test assuming eiHte^{-iHt} 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 eiHt\langle e^{-iHt}\rangle, 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)