Skip to content

Commit

Permalink
Draft working expectation_from_samples code
Browse files Browse the repository at this point in the history
To test next
  • Loading branch information
chmwzc committed Nov 3, 2023
1 parent 0f8240a commit d998987
Showing 1 changed file with 124 additions and 118 deletions.
242 changes: 124 additions & 118 deletions src/qibochem/measurement/expectation.py
Original file line number Diff line number Diff line change
@@ -1,118 +1,124 @@
# from qibo import gates
# from qibo.models import Circuit

import qibo
from qibo.hamiltonians import SymbolicHamiltonian

from qibochem.measurement.basis_rotate import measure_rotate_basis


def pauli_expectation_shots(qc, nshots):
"""
Calculates expectation value of circuit for pauli string from shots
Args:
qc: quantum circuit with appropriate rotations and measure gates
nshots: number of shots
Returns:
expectation: expectation value
"""
result = qc.execute(nshots=nshots)
meas = result.frequencies(binary=True)

bitcount = 0
for bitstring, count in meas.items():
if bitstring.count("1") % 2 == 0:
bitcount += count
else:
bitcount -= count
expectation = bitcount / nshots
return expectation


def circuit_expectation_shots(qc, of_qubham, nshots=1000):
"""
Calculates expectation value of qubit hamiltonian w.r.t. circuit ansatz using shots
Args:
qc: Quantum circuit with ansatz
of_qubham: Molecular Hamiltonian given as an OpenFermion QubitOperator
Returns:
Expectation value of of_qubham w.r.t. circuit ansatz
"""
# nterms = len(of_qubham.terms)
nqubits = qc.nqubits
# print(nterms)
coeffs = []
strings = []

expectation = 0
for qubop in of_qubham.terms:
coeffs.append(of_qubham.terms[qubop])
strings.append([qubop])

istring = 0
for pauli_op in strings:
if len(pauli_op[0]) == 0: # no pauli obs to measure,
expectation += coeffs[istring]
# print(coeffs[istring])
else:
# add rotation gates to rotate pauli observable to computational basis
# i.e. X to Z, Y to Z
meas_qc = measure_rotate_basis(pauli_op[0], nqubits)
full_qc = qc + meas_qc
# print(full_qc.draw())
pauli_op_exp = pauli_expectation_shots(full_qc, nshots)
expectation += coeffs[istring] * pauli_op_exp
## print(pauli_op, pauli_op_exp, coeffs[istring])
istring += 1

return expectation


def expectation(
circuit: qibo.models.Circuit, hamiltonian: SymbolicHamiltonian, from_samples=False, n_shots=1000
) -> float:
"""
Calculate expectation value of Hamiltonian using either the state vector from running a
circuit, or the frequencies of the resultant binary string results
Args:
circuit (qibo.models.Circuit): Quantum circuit ansatz
hamiltonian (SymbolicHamiltonian): Molecular Hamiltonian
from_samples: Whether the expectation value calculation uses samples or the simulated
state vector. Default: False, state vector simulation
n_shots: Number of times the circuit is run for the from_samples=True case
Returns:
Hamiltonian expectation value (float)
"""
if from_samples:
raise NotImplementedError("expectation function only works with state vector")
# TODO: Rough code for expectation_from_samples if issue resolved
# Yet to test!!!!!
#
# from functools import reduce
# total = 0.0
# Iterate over each term in the Hamiltonian
# for term in hamiltonian.terms:
# # Get the basis rotation gates and target qubits from the Hamiltonian term
# qubits = [factor.target_qubit for factor in term.factors]
# basis = [type(factor.gate) for factor in term.factors]
# # Run a copy of the initial circuit to get the output frequencies
# _circuit = circuit.copy()
# _circuit.add(gates.M(*qubits, basis=basis))
# result = _circuit(nshots=n_shots)
# frequencies = result.frequencies(binary=True)
# # Only works for Z terms, raises an error if ham_term has X/Y terms
# total += SymbolicHamiltonian(
# reduce(lambda x, y: x*y, term.factors, 1)
# ).expectation_from_samples(frequencies, qubit_map=qubits)
# return total

# Expectation value from state vector simulation
result = circuit(nshots=1)
state_ket = result.state()
return hamiltonian.expectation(state_ket)
import qibo
from qibo import gates
from qibo.hamiltonians import SymbolicHamiltonian
from qibo.symbols import Z

from qibochem.measurement.basis_rotate import measure_rotate_basis


def pauli_expectation_shots(qc, nshots):
"""
Calculates expectation value of circuit for pauli string from shots
Args:
qc: quantum circuit with appropriate rotations and measure gates
nshots: number of shots
Returns:
expectation: expectation value
"""
result = qc.execute(nshots=nshots)
meas = result.frequencies(binary=True)

Check warning on line 21 in src/qibochem/measurement/expectation.py

View check run for this annotation

Codecov / codecov/patch

src/qibochem/measurement/expectation.py#L20-L21

Added lines #L20 - L21 were not covered by tests

bitcount = 0
for bitstring, count in meas.items():
if bitstring.count("1") % 2 == 0:
bitcount += count

Check warning on line 26 in src/qibochem/measurement/expectation.py

View check run for this annotation

Codecov / codecov/patch

src/qibochem/measurement/expectation.py#L23-L26

Added lines #L23 - L26 were not covered by tests
else:
bitcount -= count
expectation = bitcount / nshots
return expectation

Check warning on line 30 in src/qibochem/measurement/expectation.py

View check run for this annotation

Codecov / codecov/patch

src/qibochem/measurement/expectation.py#L28-L30

Added lines #L28 - L30 were not covered by tests


def circuit_expectation_shots(qc, of_qubham, nshots=1000):
"""
Calculates expectation value of qubit hamiltonian w.r.t. circuit ansatz using shots
Args:
qc: Quantum circuit with ansatz
of_qubham: Molecular Hamiltonian given as an OpenFermion QubitOperator
Returns:
Expectation value of of_qubham w.r.t. circuit ansatz
"""
# nterms = len(of_qubham.terms)
nqubits = qc.nqubits

Check warning on line 45 in src/qibochem/measurement/expectation.py

View check run for this annotation

Codecov / codecov/patch

src/qibochem/measurement/expectation.py#L45

Added line #L45 was not covered by tests
# print(nterms)
coeffs = []
strings = []

Check warning on line 48 in src/qibochem/measurement/expectation.py

View check run for this annotation

Codecov / codecov/patch

src/qibochem/measurement/expectation.py#L47-L48

Added lines #L47 - L48 were not covered by tests

expectation = 0
for qubop in of_qubham.terms:
coeffs.append(of_qubham.terms[qubop])
strings.append([qubop])

Check warning on line 53 in src/qibochem/measurement/expectation.py

View check run for this annotation

Codecov / codecov/patch

src/qibochem/measurement/expectation.py#L50-L53

Added lines #L50 - L53 were not covered by tests

istring = 0
for pauli_op in strings:
if len(pauli_op[0]) == 0: # no pauli obs to measure,
expectation += coeffs[istring]

Check warning on line 58 in src/qibochem/measurement/expectation.py

View check run for this annotation

Codecov / codecov/patch

src/qibochem/measurement/expectation.py#L55-L58

Added lines #L55 - L58 were not covered by tests
# print(coeffs[istring])
else:
# add rotation gates to rotate pauli observable to computational basis
# i.e. X to Z, Y to Z
meas_qc = measure_rotate_basis(pauli_op[0], nqubits)
full_qc = qc + meas_qc

Check warning on line 64 in src/qibochem/measurement/expectation.py

View check run for this annotation

Codecov / codecov/patch

src/qibochem/measurement/expectation.py#L63-L64

Added lines #L63 - L64 were not covered by tests
# print(full_qc.draw())
pauli_op_exp = pauli_expectation_shots(full_qc, nshots)
expectation += coeffs[istring] * pauli_op_exp

Check warning on line 67 in src/qibochem/measurement/expectation.py

View check run for this annotation

Codecov / codecov/patch

src/qibochem/measurement/expectation.py#L66-L67

Added lines #L66 - L67 were not covered by tests
## print(pauli_op, pauli_op_exp, coeffs[istring])
istring += 1

Check warning on line 69 in src/qibochem/measurement/expectation.py

View check run for this annotation

Codecov / codecov/patch

src/qibochem/measurement/expectation.py#L69

Added line #L69 was not covered by tests

return expectation

Check warning on line 71 in src/qibochem/measurement/expectation.py

View check run for this annotation

Codecov / codecov/patch

src/qibochem/measurement/expectation.py#L71

Added line #L71 was not covered by tests


def expectation(
circuit: qibo.models.Circuit, hamiltonian: SymbolicHamiltonian, from_samples=False, n_shots=1000
) -> float:
"""
Calculate expectation value of Hamiltonian using either the state vector from running a
circuit, or the frequencies of the resultant binary string results
Args:
circuit (qibo.models.Circuit): Quantum circuit ansatz
hamiltonian (SymbolicHamiltonian): Molecular Hamiltonian
from_samples: Whether the expectation value calculation uses samples or the simulated
state vector. Default: False, state vector simulation
n_shots: Number of times the circuit is run for the from_samples=True case
Returns:
Hamiltonian expectation value (float)
"""
if from_samples:
from functools import reduce

Check warning on line 92 in src/qibochem/measurement/expectation.py

View check run for this annotation

Codecov / codecov/patch

src/qibochem/measurement/expectation.py#L92

Added line #L92 was not covered by tests

total = 0.0

Check warning on line 94 in src/qibochem/measurement/expectation.py

View check run for this annotation

Codecov / codecov/patch

src/qibochem/measurement/expectation.py#L94

Added line #L94 was not covered by tests
# Iterate over each term in the Hamiltonian
for term in hamiltonian.terms:

Check warning on line 96 in src/qibochem/measurement/expectation.py

View check run for this annotation

Codecov / codecov/patch

src/qibochem/measurement/expectation.py#L96

Added line #L96 was not covered by tests
# Get the target qubits and basis rotation gates from the Hamiltonian term
qubits = [int(factor.target_qubit) for factor in term.factors]
basis = [type(factor.gate) for factor in term.factors]

Check warning on line 99 in src/qibochem/measurement/expectation.py

View check run for this annotation

Codecov / codecov/patch

src/qibochem/measurement/expectation.py#L98-L99

Added lines #L98 - L99 were not covered by tests
# Run a copy of the original circuit to get the output frequencies
_circuit = circuit.copy()
_circuit.add(gates.M(*qubits, basis=basis))
result = _circuit(nshots=n_shots)
frequencies = result.frequencies(binary=True)

Check warning on line 104 in src/qibochem/measurement/expectation.py

View check run for this annotation

Codecov / codecov/patch

src/qibochem/measurement/expectation.py#L101-L104

Added lines #L101 - L104 were not covered by tests
# Only works for Z terms, raises an error if ham_term has X/Y terms
# total += SymbolicHamiltonian(
# reduce(lambda x, y: x*y, term.factors, 1)
# ).expectation_from_samples(frequencies, qubit_map=qubits)
# Workaround code to handle X/Y terms in the Hamiltonian:
# Get each Pauli string e.g. X0Y1
pauli = [factor.name for factor in term.factors]

Check warning on line 111 in src/qibochem/measurement/expectation.py

View check run for this annotation

Codecov / codecov/patch

src/qibochem/measurement/expectation.py#L111

Added line #L111 was not covered by tests
# Replace each X and Y symbol with Z; then include the term coefficient
pauli_z = [Z(int(element[1:])) for element in pauli]
z_only_ham = SymbolicHamiltonian(term.coefficient * reduce(lambda x, y: x * y, pauli_z, 1.0))

Check warning on line 114 in src/qibochem/measurement/expectation.py

View check run for this annotation

Codecov / codecov/patch

src/qibochem/measurement/expectation.py#L113-L114

Added lines #L113 - L114 were not covered by tests
# Can now apply expectation_from_samples directly
total += z_only_ham.expectation_from_samples(frequencies, qubit_map=qubits)

Check warning on line 116 in src/qibochem/measurement/expectation.py

View check run for this annotation

Codecov / codecov/patch

src/qibochem/measurement/expectation.py#L116

Added line #L116 was not covered by tests
# Add the constant term if present. Note: Energies (in chemistry) are all real values
total += hamiltonian.constant.real
return total

Check warning on line 119 in src/qibochem/measurement/expectation.py

View check run for this annotation

Codecov / codecov/patch

src/qibochem/measurement/expectation.py#L118-L119

Added lines #L118 - L119 were not covered by tests

# Expectation value from state vector simulation
result = circuit(nshots=1)
state_ket = result.state()
return hamiltonian.expectation(state_ket)

0 comments on commit d998987

Please sign in to comment.